wait()
同期されたブロックの外部を呼び出し、そのセマンティクスを保持して、呼び出し元のスレッドを一時停止することができた場合、潜在的な損害は何ですか?
具体的な例wait()
を使用して、同期ブロックの外部で呼び出すことができる場合に発生する問題を説明しましょう。
ブロッキングキューを実装するとします(APIにはすでに1つあります:)
(同期なしの)最初の試みは、以下の線に沿って何かを見ることができます
class BlockingQueue {
Queue<String> buffer = new LinkedList<String>();
public void give(String data) {
buffer.add(data);
notify(); // Since someone may be waiting in take!
}
public String take() throws InterruptedException {
while (buffer.isEmpty()) // don't use "if" due to spurious wakeups.
wait();
return buffer.remove();
}
}
これは潜在的に起こり得ることです:
コンシューマースレッドが呼び出しtake()
て、それを確認しbuffer.isEmpty()
ます。
コンシューマースレッドが呼び出しを続ける前にwait()
、プロデューサースレッドがやって来て、フルを呼び出しますgive()
。buffer.add(data); notify();
コンシューマースレッドはこれで呼び出しますwait()
(そして、呼び出されたばかりのスレッドを見逃します)。notify()
give()
運が悪ければ、コンシューマースレッドがウェイクアップしないという事実の結果として、プロデューサースレッドはそれ以上生成せず、デッドロックが発生します。
問題を理解すると、解決策は明らかです。との間で呼び出されないsynchronized
ようにするために使用します。notify
isEmpty
wait
詳細に立ち入ることなく:この同期の問題は普遍的です。Michael Borgwardtが指摘しているように、待機/通知はすべてスレッド間の通信に関するものであるため、常に上記と同様の競合状態になります。これが、「同期内でのみ待機」ルールが適用される理由です。
@Willieによって投稿されたリンクの段落は、それを非常によく要約しています。
ウェイターと通知者が述語の状態について合意するという絶対的な保証が必要です。ウェイターは、スリープ状態になる前のある時点で述語の状態をチェックしますが、スリープ状態になるときに述語が真であるかどうかに依存します。これらの2つのイベントの間には脆弱性の期間があり、プログラムを壊す可能性があります。
生産者と消費者が合意する必要がある述語は、上記の例にありbuffer.isEmpty()
ます。synchronized
そして、待機と通知がブロックで実行されるようにすることで、合意が解決されます。
この投稿はここの記事として書き直されました:Java:同期ブロックで待機を呼び出さなければならない理由