マルチスレッドについての解説を始める前に、いくつかの重要なポイントについて説明します:
- プロセスとスレッドの違い:
プロセス:独立したメモリ空間を持ち、プロセス内のデータ保存領域(ヒープ領域とスタック領域)は独立しています。少なくとも1つのスレッドを含みます。
スレッド:ヒープ領域は共有され、スタック領域は独立しています。スレッドが消費するリソースはプロセスよりはるかに少ないです。
-
Javaプログラムのプロセスには少なくとも2つのスレッドが含まれています。メインプロセスであるmain()メソッドスレッドと、もう一つはガベージコレクションメカニズムスレッドです。javaコマンドを使用してクラスを実行するたびに、実際にはJVMが起動され、各JVMはオペレーティングシステムでスレッドが起動されます。Javaは独自のガベージコレクションメカニズムを備えているため、Java実行時には少なくとも2つのスレッドが起動されます。
-
スレッドの作成コストはプロセスの作成コストよりはるかに小さいため、マルチタスクを実行する開発では、通常、マルチプロセスではなくマルチスレッドの作成を検討します。
-
JVMはプリエンプティブスケジューリングを採用しており、タイムシェアリングスケジューリングは採用していません。そのため、マルチスレッドの実行結果がランダムになる可能性があります。
一、スレッドの2つの作成方法:
-
Threadクラスを継承する方法
-
Runnableインターフェースを実装する方法
二、ThreadとRunnableの違い
-
クラスがThreadクラスを継承する場合、各スレッドインスタンスはそのクラスの独立したコピーを持ち、リソース共有が困難になります。一方、クラスがRunnableインターフェースを実装する場合、複数のスレッドが同じ実装クラスのインスタンスを同時に操作でき、リソース共有を容易に実現できます。
-
Runnableインターフェースを実装する利点
- リソース共有の利点:Runnableインターフェースは、複数のスレッドが同じコードロジックを実行し、同じリソースを共有するシナリオに特に適しています。複数のスレッドが同時に同じRunnable実装クラスのインスタンスを操作でき、リソースを効率的に共有できます。
- 単一継承の制限を回避:Javaは単一継承です。クラスは1つの親クラスしか継承できません。クラスがThreadクラスを継承すると、他のクラスを継承できなくなります。Runnableインターフェースを実装する場合は、このような制限を受けません。継承システムにおいてより柔軟性があります。
- プログラムの堅牢性と疎結合を強化:Runnableインターフェースを使用すると、プログラムの堅牢性が向上し、コードとスレッドの疎結合を実現できます。コードロジックはRunnable実装クラスにカプセル化され、複数のスレッドで共有できます。スレッドはタスクの実行のみを担当し、両者は独立しており、保守と拡張が容易です。
- スレッドプールへの適合:スレッドプールはJavaでスレッドを管理する重要なツールです。スレッドプールはRunnableまたはCallableインターフェースを実装したクラスのスレッドのみを受け入れ、Threadクラスを継承したスレッドは直接受け入れられません。したがって、Runnableインターフェースを実装すると、コードがスレッドプールとより良く統合できます。
三、スレッド同期メカニズム
- 同期コードブロック
//コード例
public class TicketSeller implements Runnable{
private int tickets = 100;
Object syncLock = new Object();
/*
* チケット販売操作を実行
*/
@Override
public void run() {
//各ウィンドウのチケット販売操作
//ウィンドウは常に開いている
while(true){
synchronized (syncLock) {
if(tickets > 0){//チケットがあれば販売可能
//チケット発行操作
//sleepを使用してチケット発行時間をシミュレート
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
//現在のスレッドオブジェクトの名前を取得
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "が販売中:" + tickets--);
}
}
}
}
}
- 同期メソッド
- ロックメカニズム
四、スレッドセーフなクラス
- StringBuffer。スレッドセーフで、可変の文字シーケンスです。通常、StringBuilderクラスを使用するべきです。なぜなら、それはすべての同じ操作をサポートしていますが、同期を実行しないためより速いためです。
- Vector。このクラスはListインターフェースを改良し、Java Collections Frameworkのメンバーにしました。新しいコレクション実装とは異なり、Vectorは同期されています。スレッドセーフな実装が必要ない場合は、代わりにArrayListを使用することをお勧めします。
- Hashtable。このクラスはハッシュテーブルを実装し、キーを値にマッピングします。 nullでない任意のオブジェクトをキーまたは値として使用できます。このクラスは改良され、Mapインターフェースを実装しています。新しいコレクション実装とは異なり、Hashtableは同期されています。スレッドセーフな実装が必要ない場合は、代わりにHashMapを使用することをお勧めします。
五、スレッドの優先度について
● メソッド名 ● 説明
final int getPriority()
● このスレッドの優先度を返します
final void setPriority(int newPriority)
● このスレッドの優先度を変更します。スレッドのデフォルト優先度は5です。スレッド優先度の範囲は:1-10
//数字が大きいほど優先度が高くなります
六、スレッド制御
- static void sleep(long millis):現在実行中のスレッドを指定されたミリ秒数だけ一時停止(実行を中断)させます
- void join():このスレッドの終了を待ちます
- void setDaemon(boolean on):このスレッドをデーモンスレッドとしてマークします。実行中のスレッドがすべてデーモンスレッドの場合、Java仮想マシンは終了します