スレッドのライフサイクルとjoin()メソッドの使い方。

Javaのスレッドにはライフサイクルがあって、そのライフサイクルを押さえることが、Javaのマルチスレッドプログラミングを理解する基本となります。

f:id:sho322:20140602212503j:plain

start()メソッドが呼び出されると、スレッドはまず実行可能状態に遷移します。
スレッドはスケジューラによって実行状態にされるまで実行可能状態を維持します。
スレッドが実行状態にうつったときに、run()メソッドが呼び出されます。

「実行不可能状態」には3種類あって、
・Blocked
・Sleeping
・Waiting
の3つの状態があります。

この状態については後々詳しく見ていきます(予定)

■yield()メソッド
yield()はThreadクラスのstaticメソッドです。
このメソッドは、

Thread.yield();

のように呼び出します。

yield()は同じ優先順位の他のスレッドに実行の順番を回すことを目的としています。
現在実行中のスレッドオブジェクトを一時的に休止させ、ほかのスレッドが実行できるようにします。
yieldを呼び出すと、スレッドは実行可能状態に移行します。

ほかに実行可能なスレッドがない場合や、ほかのスレッドの方が優先順位が低い場合は、ただちに実行中の女医歌いに戻ることになります。

yield()したからといって、プログラマの思い通りに順位を制御できるわけではありません。
けっこう気まぐれに実行されます。

package action;
public class Main {
public static void main(String[] args) throws InterruptedException {
Runnable runner = new Printer("one");
Thread thread1 = new Thread(runner);
thread1.start();
new Thread(new Printer("two")).start();
thread1.yield();
Thread.yield();
for(int i=0;i <100000; i++) {
System.out.print(" ");
}
System.out.println("");
System.out.println("This is main");
}
}
class Printer implements Runnable {
private String name;
public Printer(String name) {
this.name = name;
}
public void run() {
for(int i=0;i <100000; i++) {
System.out.print(" ");
}
System.out.println("");
System.out.println("This is " + name);
}
}

みたいに、適当にスレッドを作って制御しようとしても、うまくいきませんでした。

スレッドを一時停止させるには、Thread#sleep()を使います。

Thread.sleep(1000);

みたいに書くことで、1秒(1000ミリ秒)の間、そのスレッドを一時停止させることができます。

もう一つ見てみます。
join()メソッドです。
このjoin()メソッドは、指定したスレッドが終了するまでスレッドを待機させることができます。
待機中のスレッドは実行不可能な状態となり、指定したスレッドの終了を待った後、実行可能な状態になります。

さて、ここでタイトルにもあるjoin()の使い方について見ていきましょう。

package action;
public class Main {
public static void main(String[] args) throws InterruptedException {
System.out.println("This is main");
Runnable runner = new Printer("thread one");
Thread thread = new Thread(runner);
//スレッドをスタートさせる
thread.start();
//スレッドが終了するまで、"この"(=main)スレッドを待機させる
thread.join();
System.out.println("Main Thread is ended");
}
}
class Printer implements Runnable {
private String name;
public Printer(String name) {
this.name = name;
}
public void run() {
try {
//2秒眠る
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + " is ended");
}
}

上のサンプルではコメントでも書いていますが、
thread.join()をmainメソッド内で実行しています。
こうすると、main()を実行しているスレッドは、
Printerスレッドが終了するまで待機します。

で、Printerのスレッドが終わってから、mainのスレッドが動き出すようになります。

結果として、必ず以下のようにコンソールには表示されます。

This is main
thread one is ended
Main Thread is ended

パーフェクトJavaは読み返すたびに新しい発見がある名著

パーフェクトJava (PERFECT SERIES) (PERFECT SERIES 2)

パーフェクトJava (PERFECT SERIES) (PERFECT SERIES 2)

  • 作者: アリエル・ネットワーク株式会社,井上誠一郎,永井雅人,松山智大
  • 出版社/メーカー: 技術評論社
  • 発売日: 2009/09/24
  • メディア: 大型本
  • 購入: 26人 クリック: 360回
  • この商品を含むブログ (35件) を見る

久しぶりにパーフェクトJavaのスレッドの章を読み返しました。
この本は本当に、何度読んでも新しい発見がある素晴らしい本だと思う。

スレッドプールとは、作成したスレッドを待機(プール)させて、必要に応じてスレッドを取り出してタスク(Runnable)を割り当てるようなプログラミングモデルのことをいいます。