Previous | Next | Trail Map | Writing Java Programs | スレッドの制御


notify() と wait() メソッド

CubbyHole オブジェクトでget()put()メソッドは、CubbyHole に値を得たり値を置いたりすることを調整するために notify()wait()メソッドを利用する。 notify()wait()は java.lang.Object クラスのメンバである。


注釈:   notify()wait()メソッドは、同期メソッドからのみ呼び出される。


get()メソッドを調べて、CubbyHole での notify()メソッドの使用について調査してみよう。

notify() メソッド

get()メソッドは、(返す(return)以外の)最後の行いとして notify()を呼び出す。 notify()メソッドは、現在のスレッドが持つモニタで待機しているスレッドを選択して、それを呼び起こす。 典型的に、待機するスレッドはそのモニタを占有して進み出す。

プロデューサ/コンシューマの例の場合、コンシューマスレッドは get()メソッドを呼び出し、コンシューマスレッドは get()の実行中に CubbyHole のモニタを確保している。 get()メソッドの終了において、 notify()への呼び出しは CubbyHole のモニタで待機しているプロデューサスレッドを呼び起こす。 これで、プロデューサスレッドは CubbyHole モニタを得て、次に進むことができる。

public synchronized int get() {
    while (available
 == false) {
        try {
            wait();
        } catch (InterruptedException e) {
        }
    }
    available = false;
    notify();           // プロデューサに知らせる
    return seq;
}

マルチスレッドが CubbyHole モニタを待っている場合、 Java 実行時システムは待機するスレッドの1つを選択するが、どのスレッドが選択されるかについては、約束あるいは保証されない。

put()メソッドは、プロデューサがモニタを解放するのを待っているコンシューマスレッドを呼び起こすのと類似した方式で動作する。

Object クラスには、同じモニタで待機しているすべてのスレッドを呼び起こすもう1つのメソッド − notifyAll()− がある。 この状況で、呼び起こされたスレッドはモニタをめぐって競争する。 1つのスレッドがモニタを獲得すると、他方は待機することになる。

wait() メソッド

wait()メソッドは、別のスレッドが条件変更を通知するまで、現在のスレッドを(ことによると永久に)待たせる。 同じ資源を使用しているマルチスレッドの活動を調整するためには、 notify()と関連して wait()を使用する。

get()は、 available になるまでループする while 文を含む。 available が偽の場合、プロデューサがまだ新規の数を作成していないことが分かるので、コンシューマはプロデューサが数を作成するまで待機する。

while ループは wait()への呼び出しを含む。 wait()メソッドは、プロデューサスレッドからの通知をいつまでも待っている。 put()メソッドが notify()を呼び出すと、コンシューマは待ち状態から起きて、while ループ内で処理を継続する。 おそらく、プロデューサが新規の数を作成し、 get()メソッドは while ループから抜けて先に進んでいる。プロデューサがまだ数を作成していない場合、 get()はループの初めに戻り、プロデューサが新規の数を作成して notify()を呼び出すまで、待ち続ける。

public synchronized int get() {
    while (available == false) {
        try {
            wait();          // プロデューサからの notify()呼び出しを待つ
        } catch (InterruptedException e) {
        }
    }
    available = false;
    notify();
    return seq;
}

put()メソッドは、現行値を消費するためにコンシューマスレッドが待機するのと類似の方法で動作し、プロデューサが新規の数を作り出すのを許可する。

プロデューサ/コンシューマの例で使用されている、いつまでも通知を待つバージョンの他に、Object クラスは wait() メソッドの他の2つのバージョンを含む。

wait(long timeout)
通知、あるいはタイムアウトピリオドが経過するまで待機する。タイムアウトをミリ秒で測る。
wait(long timeout, int nanos)
通知、あるいはタイムアウトピリオドおよび追加のナノ秒が経過するまで待機する。

モニタと notify() と wait() メソッド

CubbyHole の put()get()メソッドに潜在的な問題があることに気付いたかも知れない。 get()メソッドの始めで、CubbyHole の値が有効でない場合(つまり、コンシューマが最後に消費して以来プロデューサが新規の数を作成しなかった場合)、プロデューサが CubbyHole に新規の値を入れるまでコンシューマは待機する。 これにより、疑問が起きる − もしコンシューマがモニタを確保している(同期メソッド get()内にあるので、 コンシューマは CubbyHole のモニタを確保する)なら、どうやってプロデューサは CubbyHole の中に新規の値を置くことができるのか?

同様に、 put() メソッドの始めで、CubbyHole の値がまだ消費されていない場合、プロデューサはコンシューマがCubbyHole で値を消費するのを待つ。そして、再度疑問が起きる − もしプロデューサがモニタを確保している(同期メソッド put()内にあるので、プロデューサは CubbyHole のモニタを確保する)なら、どうやってコンシューマは CubbyHole で値を消費することができるのか?

Java 言語の設計者もこの問題について考えた。 スレッドが wait()メソッドに入ると、 それがput()および get()メソッド両方の最初に発生した時は、モニタは極小に解放される。そしてスレッドが wait()メソッドを終了すると、モニタは再度獲得される。 これにより、待機するオブジェクトに対して、モニタを獲得する機会や、何が待機しているかによって、CubbyHole で値を消費するか CubbyHole に新規の値を作り出す機会が与えられる。


Previous | Next | Trail Map | Writing Java Programs | スレッドの制御