Javaコラム Javaエンジニアのためのお役立ちコラム

Javaのwaitでマルチスレッドプログラミングに挑戦しよう!|sleepとwaitの違いとは?

 
Javaのwaitでマルチスレッドプログラミングに挑戦しよう!|sleepとwaitの違いとは?
SE
Javaのマルチスレッドプログラミングのことについて、詳しく教えていただけませんか。
PM
''わかりました。それでは、少し詳しく解説いたしましょう。

Javaのwaitとは?


マルチスレッドプログラミングと聞いてわかる方はどの程度いらっしゃるのでしょうか。マルチスレッドとは複数の処理を表し、プログラムを平行で動作させることを意味します。普通にプログラムをすると1つの流れで順番に処理しますが、マルチスレッドなら複数の流れを同時に処理できるのです。

そういったマルチスレッドで、他のスレッドの処理が終わるまで待ちたい場合があります。Javaではそういう時には、Objectクラスのwaitメソッドで待つことができるのです。この記事ではその具体的な方法について解説します。

Javaのwaitを使用するサンプル

Javaのwaitを使うサンプルが以下です。この後、内容について全て解説します。

public class ThreadTest extends Thread {
private int number;
private Object lock;

public ThreadTest(int n, Object o) {
number = n;
lock = o;
}

public void run() {
try {
System.out.println(number + “”番のスレッド開始。””);

for (int i = 0; i < 10+(5*number); i++) {
System.out.println(number + “”番は”” + i + “”回目の作業中。””);
Thread.sleep(100);
}

switch (number) {
case 0:
synchronized (lock) {
System.out.println(number + “”番はwaitします。””);
lock.wait();
System.out.println(number + “”番はwaitが解除されました。””);
}
break;
case 1:
synchronized (lock) {
lock.notify();
System.out.println(number + “”番はnotifyをコールしました。””);
}
break;
}
} catch (InterruptedException e) {
}
}
}

public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
ThreadTest test0 = new ThreadTest(0, lock);
ThreadTest test1 = new ThreadTest(1, lock);
test0.start();
test1.start();
}

Threadクラスを継承すればマルチスレッド処理を行える

それではこのJavaサンプルを頭から説明します。まずThreadTestクラスですが、これはThreadというクラスを継承しています。これでThreadTestクラスはマルチスレッド処理を行うことができるのです。

ThreadTestクラスはコンストラクタでnumberとlockというフィールドを設定しています。numberがスレッドの番号、lockはロック用オブジェクトを格納しています。

マルチスレッド処理はtry~catchで囲む

lock用オブジェクトは、マルチスレッド間の連携処理に必要になります。これは後で説明します。

runというメソッドは親のThreadクラスの同名のメソッドをオーバーライドしています。マルチスレッド処理ではこのrunメソッド内の記述が平行で処理されます。

runの中の処理はtry~catchで囲まれていますが、マルチスレッド関連のメソッドを使用する時はThread.interruptで割り込まれてInterruptedExceptionが発生する可能性があるため、これが必要になります。

sleepとwaitの違い

runの中ではまず「〇番のスレッド開始。」と表示した後、forループで疑似的に作業をしています。numberの値が大きいほど多くループしますが、これは後でwait関連の処理を行うためです。

forループでは「〇番は△回目の作業中。」と表示した後にThread.sleepでしばらく待っています。sleepとwaitの違いは、sleepはスレッドのロックが不要でwaitはロックが必要という違いがあります。またwaitはsleepと違い、他のスレッドから待ち状態を解除されることを前提として使用します。

synchronizedで排他してからwaitする

forループとsleepによる疑似的な作業が終わった後、switch文でnumberが0か1によって違う処理を行います。0の場合はwaitをコールして待ちます。この時lockに入れたロック用オブジェクトをsynchronizedで排他して、そのオブジェクトのwaitを呼び出して待ちます。

この時に使用するロック用オブジェクトは目印のようなものだと思ってください。waitを解除するには、このロック用オブジェクトを排他しているスレッドを解除すれば良いので、処理がわかりやすくなります。

notifyもsynchronizedで排他して行う

次にswitch文でnumberが1だった場合です。ロック用オブジェクトをsynchronizedで排他して、そのオブジェクトのnotifyをコールしています。これでこのロック用オブジェクトでwaitしているスレッドがその状態から解除されるのです。

次にmainメソッドを解説します。こちらではまずlockにObject型のロックオブジェクトを生成しています。waitやnotifyはObject型にあるメソッドで、ロックオブジェクトはどんな型でもかまいません。ロックを行うためだけに存在するオブジェクトなのです。

サンプルプログラムの実行結果

mainメソッドの続きです。ロックオブジェクトの生成の次はThreadTestのインスタンスを2つ生成しています。コンストラクタで0番と1番として、ロックオブジェクトも渡しています。そして両方のstartをコールしています。これはThreadTestの親クラスのThreadのメソッドです。

これで2つのスレッドが走り出します。このJavaサンプルの実行結果は以下のようになります。

0番のスレッド開始。
1番のスレッド開始。
1番は0回目の作業中。
0番は0回目の作業中。
(中略)
0番は9回目の作業中。
1番は9回目の作業中。
0番はwaitします。
1番は10回目の作業中。
1番は11回目の作業中。
1番は12回目の作業中。
1番は13回目の作業中。
1番は14回目の作業中。
1番はnotifyをコールしました。
0番はwaitが解除されました。

waitとnotifyはペアで使用する

この実行結果を見ると分かるように、2つのスレッドは平行で同時に処理されています。そしてThreadTestのrunメソッド内のfor文で設定したように、0番の方が先に終わってwait状態になり、その後も1番はしばらく処理を行います。

そして1番がnotifyをコールすると、0番のwait状態が解除されていることが結果からわかります。このようにwaitとnotifyはペアで使用するJavaの機能なのです。

notifyAllとjoin

なおnotifyはwaitしているスレッドを1つ解除します。2つ以上あった場合も解除されるのは1つだけです。waitしている全てのスレッドを解除したい場合は、notifyAllを使用しましょう。

なおmainメソッド内でstartしたスレッドの処理を待ちたい場合は、Javaサンプルのmainの最後に以下を追加してください。これで両方のスレッドが終わるまで待つことが出来ます。

try {
test0.join();
test1.join();
System.out.println(“”全ての処理が終わりました。””);
} catch (InterruptedException e) {
}

SE
Javaのwaitとsleepの違いもよく分かりました。
PM
Javaのwaitをマスターすれば、マルチスレッドの処理を行う時に便利に活用でますよ。

マルチスレッドプログラミングではwait・notify・joinを駆使しよう

Javaのwaitやnotify、joinの使い方を解説しましたがご理解頂けましたでしょうか。

Javaのマルチスレッドプログラミングの奥は深いですが、マスターすれば高度なJavaアプリケーションを作ることができます。

Javaエンジニアとしてレベルアップするために是非身につけてください。


Javaでのキャリアアップをお考えの方は、現在募集中の求人情報をご覧ください。

また、直接のエントリーも受け付けております。

エントリー(応募フォーム)

Search

Popular

recommended

Categories

Tags