blog

コミック:sleepはロックを解放しないがwaitは解放することを証明するには?

上のコードからわかるように、同じロックが両方のwaitメソッドに適用されていますが、waitコードが呼び出された後、同じロックであるため、ロックが解放されなければnotify()は実行されません .....

Jan 7, 2021 · 4 min. read
シェア

wait ロックの例

public class WaitDemo {
 private static Object locker = new Object();
 public static void main(String[] args) throws InterruptedException {
 WaitDemo waitDemo = new WaitDemo();
 // 新しいスレッドを開始すると、メイン・スレッドがスリープしなくなる。
 new Thread(() -> {
 try {
 waitDemo.doWait();
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }).start();
 Thread.sleep(200); // この行はそれだけでは意味がない。
 waitDemo.doNotify();
 }
 /**
 * wait()を実行する
 */
 private void doWait() throws InterruptedException {
 synchronized (locker) {
 System.out.println("wait start.");
 locker.wait();
 System.out.println("wait end.");
 }
 }
 /**
 * notify()を実行する
 */
 private void doNotify() {
 synchronized (locker) {
 System.out.println("notify start.");
 locker.notify();
 System.out.println("notify end.");
 }
 }
}

上記の手順を実行した結果は

wait start.

notify start.

notify end.

wait end.

コード解析

上のコードからわかるように、wait()メソッドとnotify()メソッドの両方に同じロックがかかっていますが、wait()メソッドが呼ばれた後にロッカーのロックが解放されるので、notify()コードを普通に実行することができます。これはプリントアウトでも確認できるので、要約すると、wait() メソッドはロックを解放しています。

sleep ロックの例

public class WaitDemo {
 private static Object locker = new Object();
 public static void main(String[] args) throws InterruptedException {
 WaitDemo waitDemo = new WaitDemo();
 // 新しいスレッドを開始すると、メイン・スレッドがスリープしなくなる。
 new Thread(() -> {
 synchronized (locker) {
 try {
 System.out.println("sleep start.");
 Thread.sleep(1000);
 System.out.println("sleep end.");
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 }).start();
 Thread.sleep(200);
 waitDemo.doNotify();
 }
 /**
 * notify()を実行する
 */
 private void doNotify() {
 synchronized (locker) {
 System.out.println("notify start.");
 locker.notify();
 System.out.println("notify end.");
 }
 }
}

上記手順の実行結果は

sleep start.

sleep end.

notify start.

notify end.

コード解析

上記のコードから、sleep(1000)メソッドの実行後にnotify()メソッドを呼び出しても、ロッカー・ロックは取得されないことがわかります。 上記の実行結果から、sleep(1000)メソッドの実行後にnotify()メソッドが実行されていることがわかるので、sleep()メソッドを呼び出してもロックは解除されないことが証明できます

知識拡張

sleep synchronisedとwaitの違いは何ですか?

睡眠と待機の違いについての通常の答えはこうです:

  • waitはsynchronizeと一緒に使う必要があります;
  • 待機状態のスレッドはnotifyやnotifyAllのスレッドによって起こすことができますが、スリープ状態のスレッドはnotifyメソッドでは起こすことができません;
  • waitは通常条件付きで実行され、ある条件が真になるまでスレッドはwait状態に留まりますが、sleepはスレッドをスリープさせるだけです;
  • waitメソッドはオブジェクトのロックを解放しますが、sleepメソッドは解放しません。

waitメソッドが呼ばれた後、スレッドはWATING状態に変化し、sleepメソッドが呼ばれた後、スレッドはTIMED_WAITING状態に変化します。

2.staticメソッドでwaitは使えますか?なぜですか?

いいえ、waitメソッドはインスタンス・メソッドであるため、次のソース・コードのようにstaticでは使用できません:

public final void wait() throws InterruptedException {
 wait(0);
}

3. synchronizedを使わずにwait/notifyは使えますか?なぜですか?

いいえ、できません。synchronisedと併用しないと、次の図のようにプログラムがエラーを報告するからです:

この深い理由は、同期を取らないことで「ロスト・ウェイクアップ問題」が発生するからです:

Read next

[Flutter】パート2 Dartでコードを生成する:アノテーション、source_gen、build_runner

パート1 Dartでのコード生成:基本」では、コード生成の背後にある動機が何であるかを紹介し、コンピュータに難しい仕事をさせるためのDartの最も重要なツールのリストを紹介しました。この記事では、Dartアノテーションの作成方法と使用方法、そしてアノテーションの使用方法と作業の開始方法について説明します。...

Jan 6, 2021 · 9 min read