このページではスレッドの優先度、スレッドが取りうるライフサイクルについて説明します。 スレッドの優先度 スレッドは複数の処理を同時に動作させる仕組みのように思われがちですが、CPUが1つしかない状態においては、それは誤りです。スレッドとは複数の処理をあるスケジュールを元に実行し、複数の処理が同時に実行しているかのように見せる仕組みなのです。 あるスケジュールはそれぞれのスレッドが持つ優先度によって決定されます。Javaの優先度はプリエンプティブ型(preempt:取って代わる)と呼ばれています。つまり、現在実行しているスレッドより高い優先度を持ったスレッドが実行された場合は、現在実行しているスレッドはより高い優先度を持つスレッドにとって変わられるが、そうでない場合(より高い優先度を持つスレッドが実行されない場合)はスレッドが終了するか、実行不可状態になるまで現在実行されているスレッドが実行されつづけると言うものです。 同じ優先度を持つスレッドが複数あった場合、Javaはどちらのスレッドを実行するかを独断的に一つ選びます。選ばれなかったスレッドは選ばれたスレッドが終了するか実行不可になるまで実行されることはありません。AからZまで表示させる優先度が同じであるスレッド2つを実行した場合の結果は以下のようになります。 ABCD・・・XYZABCD・・・XYZ AABBCCなどと交互に実行されるわけではありません。Javaのプリエンプティブ型決定手順に従い同じ優先度を持つスレッドは一方が終了するか実行不可状態になるまで実行されることはありません。 Javaのスレッド実行手順は以上のようですが、Javaを動作させるOSによっては異なる実行手順を持つことがあります。それはOS自身がタイムスライシングと呼ばれる同じ優先度のスレッドを交互に実行する仕組み(場合によっては優先度の低いスレッドも実行)を持っていることがあるためです。OS上でCPUのタイムスケジュールを管理し、同じ優先度を持つ複数のスレッドをほぼ均等に実行していきます。タイムスケジュール機能を持つOS上でAからZまで表示させる優先度が同じであるスレッド2つを実行した場合は以下のようになります。 AABCBCDEDFGEFG・・・ 同じ、もしくは異なる優先度を持つスレッドにおいてOSに依存しないプログラマの意図したようにスレッドを動作させたい場合はsynchronized、waitメソッド、notifyAllメソッドを使用します。詳細はスレッドの同期を参照してください。 Threadクラスにはスレッドの優先度に関する定数、メソッドが定義されています。 【主な定数】 戻り型 定数 説明 static int MAX_PRIORITY スレッドに設定できる最高優先順位10を表します。 static int MIN_PRIORITY スレッドに設定できる最低優先順位1を表します。 static int NORM_PRIORITY スレッドに設定できるデフォルト優先順位5を表します。 【主なメソッド】 戻り型 定数 説明 int getPriority スレッドの優先順位を返します。 void setPriority スレッドの優先順位を変更します。 スレッドのライフサイクル スレッドは以下の図のようなライフサイクルで実行されます。 生成 (1).new演算子によりスレッドオブジェクトが生成されます。生成段階ではまだスレッドオブジェクトにシステム資源は割り当てられていません。 実行可能 (2).startメソッドの実行によりスレッドは実行可能状態になります。この状態になりスレッドオブジェクトにシステム資源が割り当てられます。 実行 (3).Javaを実行するシステムのスケジュール機能に従い、実行可能状態にあるスレッドのうち1つが実行(CPUが1つの場合)されます。この時、runメソッドが実行されます。(4)-1.Javaを実行するシステムのスケジュールに従い、より優先度の高いスレッドが実行されたり、タイムスケジュールにより他のスレッドが実行された場合、現在実行されているスレッドは実行可能状態となり、再び実行状態になるのを待ちます。(4)-2.yieldメソッドは同一優先度のメソッドが実行可能状態にある場合、実行状態であるyeildメソッドを呼び出したスレッドを実行可能状態にし、他の実行可能状態にあるスレッドを実行状態にするためのものです。システムのスケジュール機能にも依存しますが、より低い優先度のスレッドが実行可能状態にある場合は、yieldメソッドは無視されます。 実行不可 何らかの処理により実行状態にあるスレッドが実行状態でなくなります(実行不可状態)。この間、他のスレッドが実行状態になります。 sleep (5)-1. (6)-1. sleepメソッドを実行したスレッドはsleepメソッドで指定した時間の間実行不可状態となります。指定した時間が経過した後、そのスレッドは再び実行可能状態となり、実行状態となるのを待ちます。 wait (5)-2. (6)-2. waitメソッドを実行したスレッドは実行不可状態となり、他のスレッドからnotifyメソッド、notifyAllメソッドが呼び出されるとそのスレッドは再び実行可能状態となります。waitメソッド、notifyメソッド、notifyAllメソッドの詳細はスレッドの同期で説明します。 入出力ブロック (5)-3. (6)-3. 他のシステムからのデータの読み込みなどの入出力関連処理において、他のシステムの状況によってはその読み込み処理に多くの時間がかかる場合があります。そのような処理を実行しているスレッドは、場合によっては実行不可状態となり、他のスレッドに実行状態が取って代わります。入出力処理が終了した段階でそのスレッドは再び実行可能状態となります。 ロック (5)-4. (6)-4. スレッドがsynchronized指定された処理を実行する際、他のスレッドがすでにsynchronized指定された処理を実行していた場合、後から実行しようとしたスレッドはロックされ、実行不可状態となります。他のスレッドのsynchronized指定された処理が終了した段階でロックが解除され、再び実行可能状態となります。synchronizedの詳細はスレッドの同期で説明します。 終了 (7).runメソッドに記載される処理が終了した時点で、スレッドも終了します。1度終了したスレッドを再び実行することはできません。1度終了したスレッドオブジェクトに対し、再びstartメソッドを実行しても何も起きません。 前ページ(はじめてのスレッド)での例題を元に、スレッドのライフサイクルについて説明します。 【例1】2つのスレッドを平行して実行させ、1から10までカウントするプログラムです。 class ExThread1 extends Thread{ //(3)スレッドの実行 public void run() { for(int i = 1; i <= 10; i++) { System.out.println(getName() + ":" + i); try { //(4)sleep状態へ移行 sleep(1000); } catch (InterruptedException e) { } } } //(5)スレッドの終了 public static void main(String[] args){ //(1)スレッドの作成 ExThread1 thread1 = new ExThread1(); ExThread1 thread2 = new ExThread1(); //(2)実行可能状態へ移行 thread1.start(); thread2.start(); } } 生成 (1).new演算子によりスレッドオブジェクトthread1、thread2を生成しています。 実行可能 (2).startメソッドを実行し、スレッドを実行可能状態に移行します。 実行 (3).Javaを実行するシステムのスケジュール機能に従い、実行可能状態のスレッドの内の1つが実行されます。実行時はrunメソッド内のコードが実行されます。 実行不可:sleep (4).sleepメソッドを実行し、メソッドを実行したスレッドが1秒間sleep状態となります。1秒経過後、再び実行可能状態となり、システムのスケジュール機能に従い、実行状態となるのを待ちます。Javaを実行するシステムのスケジュール機能が必ずしもthread1オブジェクトとthread2オブジェクトを交互に実行するとは限りません。同一優先度のスレッドが複数存在する場合において、システムのスケジュール機能がそのうちの1つのスレッドの実行を独断的に決定していた場合、thread1オブジェクトとthread2オブジェクトの実行順序は予想できないものとなります。 終了 (5).runメソッドが終了した時点で、スレッドも終了します。 【実行結果1】 1度目実行 D:\JAVA>java ExThread1 Thread-1:1 Thread-2:1 Thread-1:2 Thread-2:2 Thread-1:3 Thread-2:3 Thread-1:4 Thread-2:4 Thread-1:5 Thread-2:5 Thread-1:6 ← システムのスケジュール機能がスレッドを独断的に Thread-2:6 ← 決定しているため、実行ごとに実行結果が異なる。 Thread-2:7 Thread-1:7 Thread-2:8 Thread-1:8 Thread-2:9 Thread-1:9 Thread-2:10 Thread-1:10 D:\JAVA> 2度目実行 D:\JAVA>java ExThread1 Thread-1:1 Thread-2:1 Thread-1:2 Thread-2:2 Thread-1:3 Thread-2:3 Thread-1:4 Thread-2:4 Thread-1:5 Thread-2:5 Thread-2:6 ← システムのスケジュール機能がスレッドを独断的に Thread-1:6 ← 決定しているため、実行ごとに実行結果が異なる。 Thread-2:7 Thread-1:7 Thread-2:8 Thread-1:8 Thread-2:9 Thread-1:9 Thread-2:10 Thread-1:10 D:\JAVA>