71

wait()がすべてのロックを解除するという印象を受けましたが、この投稿を見つけました。

「同期メソッド内で待機を呼び出すことは、固有のロックを取得する簡単な方法です」

少し混乱していることを明確にしてください。

http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

4

4 に答える 4

167

「同期メソッド内で待機を呼び出すことは、固有のロックを取得する簡単な方法です」

この文は誤りです。ドキュメントの誤りです。

スレッドは、同期メソッドに入ると、固有のロックを取得します。同期されたメソッド内のスレッドは、ロックの所有者として設定され、RUNNABLE状態になります。ロックされたメソッドに入ろうとするスレッドはすべてBLOCKEDになります。

スレッドが待機を呼び出すと、現在のオブジェクト ロックが解放され (他のオブジェクトからのすべてのロックが保持されます)、WAITING状態になります。

他のスレッドがその同じオブジェクトで notify または notifyAll を呼び出すと、最初のスレッドの状態が WAITING から BLOCKED に変わります。通知されたスレッドは自動的にロックを再取得したり、RUNNABLE になったりしません。実際、他のすべてのブロックされたスレッドとロックを争う必要があります。

WAITING 状態と BLOCKED 状態はどちらもスレッドの実行を妨げますが、大きく異なります。

待機中のスレッドは、他のスレッドからの通知によって明示的に BLOCKED スレッドに変換する必要があります。

WAITING が直接 RUNNABLE になることはありません。

RUNNABLE スレッドが (モニターを終了するか待機することにより) ロックを解放すると、BLOCKED スレッドの 1 つが自動的に代わりになります。

要約すると、スレッドは、同期メソッドに入るとき、または待機に同期メソッドに再び入るときにロックを取得します。

public synchronized guardedJoy() {
    // must get lock before entering here
    while(!joy) {
        try {
            wait(); // releases lock here
            // must regain the lock to reentering here
        } catch (InterruptedException e) {}
    }
    System.out.println("Joy and efficiency have been achieved!");
}
于 2012-12-01T21:51:28.133 に答える
8

wait:: はjava.lang.Objectクラスの一部であるため、このメソッドはオブジェクトでのみ呼び出すことができます。これを呼び出すには、そのオブジェクトでmonitor(lock)が必要です。それ以外の IllegalMonitorStateException場合はスローされます。例) Thread.currentThread().wait() は、以下のコードでこの例外をスローします。

   Example1
   public void doSomething() {                                          Line 1
        synchronized(lockObject) { //lock acquired                      Line 2
            lockObject.wait();     // NOT Thread.currentThread().wait() Line 3
        }
    }

行 3 で wait を呼び出すと、行 2 で取得したロックが解放されます。したがって、行 1 に入り、ロックの取得を待機している他のスレッドは、lockObjectこのロックを取得して続行します。

これを考えてみましょうExample2。ここではlockObject2ロックのみが解放され、現在のスレッドは引き続きlockObject1ロックを保持します。これはデッドロックにつながります。そのため、ユーザーはこの場合により注意する必要があります。

   Example2 
        public void doSomething() {                                     Line 1
             synchronized(lockObject1) { //lock1 acquired               Line 2
                 synchronized(lockObject2) { //lock2 acquired           Line 3
                     lockObject2.wait();                                Line 4
                 }
             }
        }

この待機が置き換えられた場合sleep, yield, or join、ロックを解放する機能がありません。保持しているロックを解放できるのは待機だけです。

静的 api の場所に注意してくださいt1.sleep()/t1.yield()。常にアクションはcurrentThread非スレッドで実行されますt1

suspend次に、これらの API との違いを理解しましょうsleep, yield, join。スレッドがロックを保持している状況を回避するためsuspendに非推奨になっているため、スレッドが未定義の時間中断された (実行状態ではない) 場合にデッドロックが発生します。これは、他の API でも同じ動作です。

答えは、サスペンド/レジュームはt1.suspend()、これらの API が Thread.currentThread(). したがって、デッドロックを回避するために、これらの API を呼び出す前にロックを保持しないように注意する必要があります。を呼び出す場合はこの限りではありませんsuspend。呼び出し先スレッドは、サスペンドを実行しようとしている呼び出し元スレッド (ロック) の状態を認識していないため、非推奨です。

于 2016-06-15T06:34:10.497 に答える
2

この声明は、その完全な文脈の中で見られるべきだと思います。

スレッドが d.wait を呼び出すとき、スレッドは d の固有ロックを所有している必要があります — そうでない場合、エラーがスローされます。同期されたメソッド内で待機を呼び出すことは、固有のロックを取得する簡単な方法です。

これを次のように単純化する必要があることを理解しています。

メソッドの呼び出しはオブジェクトのロックを取得します。呼び出しをメソッド内にsynchronized置くだけです。wait()synchronized

于 2015-11-18T19:43:24.843 に答える