私の答えには、実装固有の情報がいくつかあります。これは、Sun JVM およびその他のスレッド ライブラリの動作に関する私の実務知識に基づいています。
2 つのプロデューサー スレッドが通知を呼び出した場合、2 つの異なる待機中のコンシューマー スレッドが起動されることが保証されますか?
いいえそうではありません。目覚めた消費者がいるという保証はありません。保証されているのは、待機中のスレッドが 2 つある場合、2 つの異なるスレッドが実行キューに入れられることです。
それとも、2 つnotify()
の s が互いに直後に起動されると、同じコンシューマ スレッドがウェイクアップのために 2 回キューに入れられる可能性がありますか?
いいえ。2 つのnotify()
呼び出しによって、同じコンシューマー スレッドが 2 回キューに入れられることはありません。ただし、1 つのスレッドが起動され、他のスレッドが待機していない可能性があるため、2 番目のnotify()
呼び出しは何もしない可能性があります。もちろん、スレッドが目覚めた後、すぐに戻って再び待機し、2番目のnotify()
呼び出しをそのように取得することもできますが、それがあなたが求めていることだとは思いません。
Javaには、スレッドを1回だけ起動するためのアトミックな内部操作がありますか?
はい。Thread
コードには多数の同期ポイントがあります。スレッドが通知されると、wait
キューから移動されます。今後の呼び出しnotify()
はwait
キューを調べますが、スレッドは見つかりません。
もう1つの重要なポイント。while
生産者/消費者モデルでは、ループで条件をテストしていることを常に確認してください。その理由は、ロックでブロックされているが条件を待機していないコンシューマーとの競合状態があるためです。
synchronized (workQueue) {
// you must do a while here
while (workQueue.isEmpty()) {
workQueue.wait();
}
workQueue.remove();
}
Consumer1
を待っている可能性がありますworkQueue
。 実行キューでConsumer2
ブロックされる可能性があります。synchronized
andに何かを入れるworkQueue
とworkQueue.notify()
呼び出されます。 Consumer2
は現在実行キューに入れられていますが、最初にそこにいた人より遅れています。 Consumer1
これは一般的な実装です。そのため、通知され たConsumer1
からアイテムを削除します。空であるかどうかを再度テストする必要があります。そうでない場合は、キューが再び空になるため、スローされます。レースの詳細はこちら。workQueue
Consumer2
Consumer2
workQueue
remove()
また、偽のウェイクアップが文書化されているため、呼び出しwhile
なしでスレッドが起動されるのをループが防止することを認識することも重要です。wait()
BlockingQueue
これはすべて、他の回答で推奨されているようにを使用してプロデューサー/コンシューマーコードを削減できる場合は、そうする必要があります。このBlockingQueue
コードは、これらの問題をすべて解決しています。