Javaでの待機と通知の誤ったウェイクアップ問題

序文

このブログはhttps://www.cnblogs.com/clover-forever/p/12616869.htmlからのものです 

後で確認できるように、ここに書き留めてください。

偽覚醒の概念

jdkの公式ドキュメントは次のように説明しています:

 

 

 

したがって、待機と通知を一緒に使用する場合ifを条件として使用すると、誤ったウェイクアップが発生するため、whileをループ条件として使用する必要があります以下は実験の例です。

最初に、リソースクラスを作成します(マルチスレッドでは、リソースクラスとスレッド操作は通常、同じクラスではなく分離されます。スレッドがリソースクラスで動作する場合のみ、リソースクラスのオブジェクトが作成されます)

パッケージcom.test; 

/ **
*リソースクラス
* @author Huxudong
* @createTime 2020-04-01 21:57:39
** /
パブリッククラスリソース{
/ **製品番号* /
private int product = 0;

/ * *購入* /
public synchronized void get(){
if(product> = 10){
System.out.println(Thread.currentThread().GetName()+ ":" + "Product is full!");
/ **製品がいっぱいになると、購入スレッドがハングします* /
try {
this.wait();
} catch(InterruptedException e){
e.printStackTrace();
}
}

/ **購入* /
System.out.println(Thread.currentThread().GetName()+ ":" + ++ product);
/ **他のスレッドを起こす* /
this.notifyAll();

}

/ **セール* /
public synchronized void sale(){
if(product <= 0){
System.out.println(Thread.currentThread()。getName()+ ":" + "product is empty");
try {
this.wait();
} catch( InterruptedException e){
e.printStackTrace();
}
}

/ **販売* /
System.out.println(Thread.currentThread()。GetName()+ ":" + --product);
/ **他のスレッドを起動する* /
this.notify();
}
}

次に、リソースクラスを操作するスレッドを作成します(java8新機能のラムダ式を介して直接作成されます)

パッケージcom.test; 

import java.util.concurrent.TimeUnit;

/ **
*スレッドとリソースクラスの分離を実現するスレッド操作リソースクラス
* @author Huxudong
* @createTime 2020-04-01 23:13:54
** /
public class TestPc {
public static void main(String [] args){
Resource resource = new Resource();
new Thread(()-> {
for(int i = 0; i <20; i ++){
try {
/ * *簡単に観察できるようにスリープ* /
TimeUnit.SECONDS.sleep(2);
} catch(InterruptedException e){
e.printStackTrace();
}
resource.get();
}
}、 "Producer A")。Start();



新しいスレッド(()-> {
for(int i = 0; i <20; i ++){
resource.sale();
}

}、 "Consumer C") .start();

新しいスレッド(()-> {
for(int i = 0; i <20; i ++){

resource.get();
}

}、 "producer B")。start();

新しいスレッド( ()-> {
for(int i = 0; i <20; i ++){
resource.sale();
}

}、 "Consumer D")。start();
}
}

最初に見てみましょう何が起こったのか:

 

 

はい、あなたはそれを正しく読みました、どうして負の数があるのでしょうか?これは間違いです。落ち着いて、まだ少し手掛かりで、問題がどこで発生したかを分析します(そして、あなたは驚いていない、非常に強力な人です)。

分析してみましょう。最初に、コンシューマCおよびDスレッドが呼び出されました(プロデューサでスリープを記述したため)。コンシューマは、この時点で製品リソースが0であることを発見したため、コンシューマCおよびDブラザー、待機メソッドを呼び出してスリープし、ロックを解除する方法はありません。

しかし、この時点で最初の消費者が目覚め、エンジンが製品の生産を開始し、生産後、待機中のすべての消費者スレッドが目覚めました。CとDの2人の兄弟がついに目を覚まし、Dが最初にロックを取得したので、彼らは最初に製品を消費し、次に製品がないことを発見し、悲しそうに眠りましたが、現時点では忘れないでくださいCのバディが目覚めました。あなたは他の人を目覚めさせました。他に何をしましたか?それ以外の場合、それはとても不快です。残念ながら、現時点での判断条件はifなので、Cのバディは現時点では影響を受けません。条件付き制約に続いて、上記のスリープコードの実行により、別の製品を消費することを断固として決定しました。Dバディが消費した後、それはすでに0であることがわかります。このCバディは、マイナス1を消費しますが、-1ですか? 、など。判断条件が良くない場合、この時に目覚めたバディCは、誤った目覚めと同等であり、プログラムに予期せぬエラーをもたらすことがわかります。ここで判断しながらwhileを使う必要があるので、まずifをwhileで置き換えた結果を見てみましょう。

 今回はより正常な結果ですが、なぜそれを使用するのですか?上記のように、すべてのコンシューマスレッドが起こされても、whileループはこの時点で判断を続けます。バディは外に出られないため、戻って次の消費者製品削減操作を実行しないため、このエラーは回避されます。これは、waitおよびnotifyAllを使用する場合にも公式に推奨され、whileループ条件判断を使用する必要があります。

おすすめ

転載: www.cnblogs.com/jichi/p/12694260.html