スレッドの同期(wait・notify・notifyall)
Javaは、スレッドをある状態で待機させ、別のスレッドから通知が来た時点で処理を再開するといったしくみを作ることができます。
そのようなしくみは、java.lang.Objectクラスのwait()メソッド、notify()メソッド、notifyAllメソッドを使うことで実現できます。
これらのメソッドを使ったサンプルを下記に示します。
上記サンプルはJavaアプレットとして定義しています。
まず、アプレットが開始・再開されるときに呼ばれるstart()メソッド内で新しいスレッドを作成し、起動させています。
→ 動作確認
スレッドが起動することで呼ばれるrun()メソッド内ではfor文で5回ループさせています。
そのforブロック内にあるaddCount()メソッドに着目してください。
count変数をインクリメントさせたら、wait()メソッドを呼び出しています。
wait()メソッドが呼び出された時点でこのスレッドは待機状態になります。
待機状態であるスレッドを再開させるには、notify()メソッドかnotifyAll()メソッドを別のスレッドから呼び出す必要があり、その処理をreStart()メソッドで行っています。
reStart()メソッドはマウスをクリックしたときに呼ばれるmousePressed()メソッドから呼び出しています。
これで、wait()メソッドによって待機状態であったスレッドが再開します。
ここで、wait()メソッド、notifyAll()メソッドを呼び出しているメソッドに、synchronizedが指定されていることに着目してください。
wait()メソッド、notify()メソッド、notifyAll()メソッドを呼び出すためには、スレッドは該当するオブジェクトのロックを取得しておく必要があります。
そのため、synchronized指定をはずすとコンパイルエラーとなります。
詳しくは → スレッドの排他制御(synchronized)
サンプルはアプレット画面をクリックする度に待機中であるスレッドを再開させており、それを5回繰り返しています。
実際にサンプルを動かしてみてプログラムの動作を確認してみてください。
そのようなしくみは、java.lang.Objectクラスのwait()メソッド、notify()メソッド、notifyAllメソッドを使うことで実現できます。
wait()
他のスレッドがこのオブジェクトのnotify() メソッドまたは、
notifyAll()メソッドを呼び出すまで、現在のスレッドを待機させます。
notify()
wait()メソッドによって待機中であるスレッドの1つを再開します。
再開するスレッドは、Java仮想マシンによって任意に選ばれ、
プログラム側から指定することはできません。
notifyAll()
wait()メソッドによって待機中であるすべてのスレッドを再開します。
これらのメソッドを使ったサンプルを下記に示します。
import java.applet.*;
import java.awt.Graphics;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
public class WaitSample extends Applet implements Runnable, MouseListener{
private Thread th;
private int width;
private int height;
private int count;
private String countString = "";
public void init(){
width = getSize().width; //Javaアプレット画面横幅サイズ取得
height = getSize().height; //Javaアプレット画面縦幅サイズ取得
addMouseListener(this); //マウスイベントを受け取り準備
}
public void start(){
count = 0;
th = new Thread(this);
th.start();
}
public void mouseClicked(MouseEvent e){
}
public void mouseEntered(MouseEvent e){
}
public void mouseExited(MouseEvent e){
}
public void mousePressed(MouseEvent e){
this.reStart();
}
public void mouseReleased(MouseEvent e){
}
synchronized public void reStart(){
notifyAll();
}
synchronized public void addCount(){
count++;
try{
wait();
}catch(Exception e){}
}
public void run(){
for(int i = 0; i < 5; i++){
addCount();
repaint();
}
countString = "スレッド終了";
}
public void paint(Graphics g){
g.drawString("" + count, 50,50);
g.drawString(countString, 20,60);
}
}
上記サンプルはJavaアプレットとして定義しています。
まず、アプレットが開始・再開されるときに呼ばれるstart()メソッド内で新しいスレッドを作成し、起動させています。
→ 動作確認
スレッドが起動することで呼ばれるrun()メソッド内ではfor文で5回ループさせています。
そのforブロック内にあるaddCount()メソッドに着目してください。
synchronized public void addCount(){
count++;
try{
wait();
}catch(Exception e){}
}count変数をインクリメントさせたら、wait()メソッドを呼び出しています。
wait()メソッドが呼び出された時点でこのスレッドは待機状態になります。
待機状態であるスレッドを再開させるには、notify()メソッドかnotifyAll()メソッドを別のスレッドから呼び出す必要があり、その処理をreStart()メソッドで行っています。
reStart()メソッドはマウスをクリックしたときに呼ばれるmousePressed()メソッドから呼び出しています。
synchronized public void reStart(){
notifyAll();
}これで、wait()メソッドによって待機状態であったスレッドが再開します。
ここで、wait()メソッド、notifyAll()メソッドを呼び出しているメソッドに、synchronizedが指定されていることに着目してください。
wait()メソッド、notify()メソッド、notifyAll()メソッドを呼び出すためには、スレッドは該当するオブジェクトのロックを取得しておく必要があります。
そのため、synchronized指定をはずすとコンパイルエラーとなります。
詳しくは → スレッドの排他制御(synchronized)
サンプルはアプレット画面をクリックする度に待機中であるスレッドを再開させており、それを5回繰り返しています。
実際にサンプルを動かしてみてプログラムの動作を確認してみてください。
