序文
このブログは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ループ条件判断を使用する必要があります。