いいえ、Object::wait
呼ばれません。wait
/メカニズムは、notify
によって提供される基本的なロックの上にある追加のレイヤーsynchronized
です。またはを使用しsynchronized
なくても使用できます。wait
notify
基本的なsynchronized
メカニズムは、特定のオブジェクトに取り付けられたロックをロックおよびロック解除するという考え方に基づいています (ロックはモニターと呼ばれることもあります)。1 つのスレッドがロックをロックすると、それをロックしようとする別のスレッドがブロックされます。最初のスレッドがロックを解除すると、2 番目のスレッドがブロックを解除し、引き続きロックをロックします。
wait
/メカニズムは、スレッドが保持しているロックを一時的に放棄する方法を提供します。notify
その間、ロックを保持している他のスレッドによってロックの再取得が制御されます。次のコードを検討してください。
public synchronized void first() {
System.out.println("first before");
wait();
System.out.println("first after");
}
public synchronized void second() {
System.out.println("second before");
notify();
System.out.println("second after");
}
あるスレッド、スレッド A が を呼び出しfirst
、次に別のスレッド B が を呼び出しているとしますsecond
。イベントのシーケンスは次のとおりです。
- A がロックを取得しようとする
- A がロックの取得に成功する
- A は System.out に「first before」を書き込みます
- B がロックの取得を試みる
- B はロックを取得できません。A がロックを取得しているため、ブロックされます。
- A が書き込みを終了し、呼び出します
wait
。この時点で、Aはロックを解放し、代わりに待機を開始します。
- B はロックの取得に成功し、ブロックを解除します
- B は System.out に「秒前」を書き込みます。
- B が書き込みを終了し、呼び出します。
notify
これは B には影響しませんが、Aが待機を停止し、ロックを再取得しようとすることを意味します。
- Bがロックを取得しているため、Aはロックを取得できないため、ブロックされます
- B は System.out に「秒後」を書き込みます。
- B はメソッドを終了し、ロックを解除します
- A はロックの取得に成功し、ブロックを解除します
- A は続行し、System.out に「first after」を書き込みます。
- A はメソッドを終了し、ロックを解放します
これは長々と説明しましたが、実際には非常に単純なプロセスです。wait
/notify
呼び出しは、最初のスレッドが別のスレッドにロックを貸して使用できるようにするようなものです。
2 種類のブロッキングが行われていることを認識することが重要です。synchronized
まず、スレッドがブロックに入るとき(または呼び出しから戻ったときにブロックを再入力するとき) に、スレッドがロックを取得するのをブロックする方法wait
。次に、 を呼び出した後wait
、対応する によってブロック解除される前に、スレッドがブロックする方法notify
。
私は、wait
/notify
が別のスレッドにロックを貸すスレッドであると説明しました。これは私が考える方法であり、生産的な比喩だと思います。時事的に不気味な比喩を使うと、おそらくそれは吸血鬼が城に移動し、棺桶の中で眠りにつくようなものです. 彼が眠りにつくと、無実の観光客がやって来て、城を別荘として貸し出します。ある時点で、訪問者は地下室を探索し、棺桶を邪魔します。その時点で、吸血鬼は目を覚まし、城を取り戻したいと思っています. 観光客が恐怖で逃げたら、彼は家に戻ることができます。
とのような名前ではなく、 と という名前が付けられているwait
理由は、これらが通常、スレッド間通信メカニズムを構築するために使用されるためです。そこでは、最初のスレッドによるロックの最初の貸し出しではなく、目覚めに重点が置かれます。 2番目のスレッドによるウェイターの。notify
lend
return
さて、最後に 2 番目の質問に移りますが、考えるべきことが 2 つあります。
1 つ目は、「誤ったウェイクアップ」の可能性です。セクション17.2.1 のネストされた箇条書きリストの奥にある小さなメモを参照してください。Java 言語仕様の待機:
[...] 実装による内部アクションのため、スレッドは待機セットから削除される可能性があります。推奨はされていませんが、実装で「偽のウェイクアップ」を実行すること、つまり待機セットからスレッドを削除して、明示的な指示なしに再開できるようにすることは許可されています。
つまり、スレッドは通常、通知された場合にのみウェイクアップしますが、通知されていないときにランダムにウェイクアップする可能性があります。wait
したがって、例とまったく同じように、条件変数のチェックを含むループで aを保護する必要があります。仕様が言うように:
この規定により、スレッドが待機している何らかの論理条件が保持される場合にのみ終了するループ内でのみ待機を使用するという Java コーディングの慣行が必要になることに注意してください。
2つ目は中断です。中断はランダムではありません。interrupt
割り込みは、待機中のスレッドで他のスレッドが呼び出された場合にのみ発生します。これが発生すると、すぐにブロックを停止しInterruptedException
、wait
呼び出しをスローします。これまで見てきたこととは反対に、この例外をキャッチして再度待機するのは正しくありません。その理由は非常に単純です。誰かがinterrupt
あなたのスレッドに電話をかけてきたら、それはまさに彼らがあなたに電話してほしいからです。stop waiting
! 代わりにスレッドが何をすべきかを正確に言うことは不可能ですが、通常のアプローチは、現在の作業を中止し、制御を呼び出し元に戻すことです。現在の作業が中止された後に呼び出し元が続行できない場合は、呼び出し元も中止する必要があり、適切な処理ができるレベルに達するまで呼び出しスタックを上に上げます。割り込みの正しい処理は、ここで扱うには大きすぎるテーマですが、チュートリアルの割り込みのサポートについて読むことから始め、可能であればJava Concurrency In Practiceを読んでください。