オブジェクトwait():スレッドを待機状態にし、CPUを解放し、リソースをロックします
JavaDocのwaitメソッドの説明は次のとおりです。
現在のスレッドを、別のスレッドが呼び出すまで待機させます
現在のスレッドは、このオブジェクトのモニターを所有している必要があります。//それ以外の場合:IllegalMonitorStateException(待機中のオブジェクトはロックされていません)
言い換えると、待機は常に次のようにループで発生する必要があります。//(失虚假唤起偽のウェイクアップ)
synchronized (obj) {
while (<condition does not hold>) {
obj.wait(timeout);
}
... // Perform action appropriate to condition
}
スレッドがwaitメソッドを実行して待機に入ると、オブジェクトの待機中のコレクションに入り、次の4つの状況でウェイクアップされます。
1.他のスレッドがクラスのnotifyメソッドを呼び出し、現在のスレッドがウェイクアップスレッドとして選択された場合
2.他のスレッドはnotifyAll()メソッドを呼び出します
3.スレッドが他のスレッドによって中断されている
4.待機が最大待機時間に達しました。この場合、時間が0に設定されていると、スレッドは永久に待機します。
Javadocには、waitメソッドを使用する場合、上記の4つのメソッドを通過せずに待機がウェイクアップされる可能性が低いため、待機をループループに入れるのが最適であると具体的に記載されています。このウェイクアップは疑似ウェイクと呼ばれます。
待機がループに書き込まれない場合、スレッドが異常に実行される可能性があります
スプリアスウェイクアップ
http://tutorials.jenkov.com/java-concurrency/thread-signaling.html
不可解な理由により、notify()およびnotifyAll()が呼び出されていなくても、スレッドがウェイクアップする可能性があります。これは、スプリアスウェイクアップとして知られています。理由もなく目覚めます。(唤興尚尚确)
MyWaitNofity2クラスのdoWait()メソッドで誤ったウェイクアップが発生した場合、待機中のスレッドは、適切なシグナルを受信せずに処理を続行する可能性があります。これにより、アプリケーションに重大な問題が発生する可能性があります。
誤ったウェイクアップを防ぐために、シグナルメンバー変数はifステートメント内ではなくwhileループ内でチェックされます。このようなwhileループは、スピンロックとも呼ばれます。目覚めたスレッドは、スピンロック(whileループ)の条件がfalseになるまでスピンします。これを示すMyWaitNotify2の修正バージョンは次のとおりです。
public class MyWaitNotify3{
MonitorObject myMonitorObject = new MonitorObject();
boolean wasSignalled = false;
public void doWait(){
synchronized(myMonitorObject){
while(!wasSignalled){
try{
myMonitorObject.wait();
} catch(InterruptedException e){...}
}
//clear signal and continue running.
wasSignalled = false;
}
}
public void doNotify(){
synchronized(myMonitorObject){
wasSignalled = true;
myMonitorObject.notify();
}
}
}
wait()呼び出しがifステートメントではなくwhileループ内にネストされていることに注目してください。待機中のスレッドがシグナルを受信せずにウェイクアップした場合、wasSignalledメンバーは引き続きfalseになり、whileループがもう一度実行され、ウェイクアップされたスレッドは待機状態に戻ります。
プログラムがwait()メソッドを呼び出すと、WAITING状態になります。 wasSignalled 值设置为 true 后被 notify()
誤って目覚めた場合、この時点で wasSignalled值依然为(false),不满足预期, 所以需要对预期条件重复校验,即 while(!wasSignalled)