wait()がすべてのロックを解除するという印象を受けましたが、この投稿を見つけました。
「同期メソッド内で待機を呼び出すことは、固有のロックを取得する簡単な方法です」
少し混乱していることを明確にしてください。
http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
wait()がすべてのロックを解除するという印象を受けましたが、この投稿を見つけました。
「同期メソッド内で待機を呼び出すことは、固有のロックを取得する簡単な方法です」
少し混乱していることを明確にしてください。
http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
「同期メソッド内で待機を呼び出すことは、固有のロックを取得する簡単な方法です」
この文は誤りです。ドキュメントの誤りです。
スレッドは、同期メソッドに入ると、固有のロックを取得します。同期されたメソッド内のスレッドは、ロックの所有者として設定され、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!");
}
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
。呼び出し先スレッドは、サスペンドを実行しようとしている呼び出し元スレッド (ロック) の状態を認識していないため、非推奨です。
この声明は、その完全な文脈の中で見られるべきだと思います。
スレッドが d.wait を呼び出すとき、スレッドは d の固有ロックを所有している必要があります — そうでない場合、エラーがスローされます。同期されたメソッド内で待機を呼び出すことは、固有のロックを取得する簡単な方法です。
これを次のように単純化する必要があることを理解しています。
メソッドの呼び出しはオブジェクトのロックを取得します。呼び出しをメソッド内に
synchronized
置くだけです。wait()
synchronized