java中wait和notify的虛假喚醒問題

前言

本篇博客來自 http://www.javashuo.com/article/p-aqumdytb-ba.htmlhtml

本身在此記錄一下,方便往後複習。java

虛假喚醒的概念

jdk官方文檔解釋:多線程

 

 

 

因此說在wait和notify一塊使用時,若是使用if做爲條件時,會有虛假喚醒的狀況發生,因此必須使用while做爲循環條件。下面來舉例實驗:this

首先,建立一個資源類:(在多線程中,通常都是資源類和線程操做解耦,不放在用同一個類中,只有在線程操做資源類時,纔會建立資源類的對象)線程

package com.test;

/**
* 資源類
* @author Huxudong
* @createTime 2020-04-01 21:57:39
**/
public class Resource {
/** 產品數 */
private int product = 0;

/** 進貨 */
public synchronized void get() {
if(product >= 10) {
System.out.println(Thread.currentThread().getName()+":"+"產品已滿!");
/** 當商品已經滿的時候,進貨線程掛起 */
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()+":"+"產品已空");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

/** 售貨 */
System.out.println(Thread.currentThread().getName()+":"+ --product);
/** 喚醒其餘線程 */
this.notify();
}
}

而後再建立線程來操做咱們的資源類(經過java8新特性Lambda表達式直接建立)

package 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();
}
},"生產者A").start();



new Thread(()->{
for (int i = 0; i < 20; i++) {
resource.sale();
}

},"消費者C").start();

new Thread(()->{
for (int i = 0; i < 20; i++) {

resource.get();
}

},"生產者B").start();

new Thread(()->{
for (int i = 0; i < 20; i++) {
resource.sale();
}

},"消費者D").start();
}
}

先來看看若是使用if條件會發生什麼:

 

 

對,你沒看錯,怎麼可能會出現負數呢,這確定是不對的。冷靜下來分析一下,仍是有點頭緒,知道哪裏出現了問題的(那你是一個處事不驚的人,很厲害)。3d

來,分析一下,一開始先調用了消費者C,D線程(由於咱們寫了睡眠在生產者中),消費者此時發現此時product資源爲0,因此,消費者C,D這兩個兄弟,沒辦法只能調用wait方法,睡眠了,而且釋放了鎖。htm

可是此時第一個消費者已經甦醒了,發動機開始生產產品了,而且生產以後,又喚醒了全部等待的消費者線程。這消費者C,D兩兄弟終於甦醒了,D哥們先得到了鎖,因此就先消費了一個產品,而後就又發現沒有產品了,又傷心的休眠去了,可是不要忘了,此時還有一個C哥們被喚醒了啊,你喚醒了人家,人家總的乾點什麼事情吧,否則這多難受,恰好不巧的是,此時的判斷條件是if,因此此時C哥們便不受條件的約束,接着上面本身睡眠的代碼處執行,毅然決然的又去消費了一個產品,原來D哥們消費後,就已經爲0了,這個C哥們再去消費減一,不就是-1了嗎,以此類推分析。發現若是判斷條件用很差,此時喚醒的C哥們就至關於虛假喚醒的了,會給程序帶來不可預估的錯誤。因此在這裏判斷必需要使用while,先來看看把if換成while的結果。對象

 這回結果就比較正常了,爲何使用while就能夠呢,由於像上文所說,即便喚醒了全部的消費者線程,此時會不停while循環判斷,若是此時條件是爲0,那麼C哥們就不能出while,那麼他也就不回執行下面消費產品的減減操做了,那麼就會避免了這種錯誤。這也是官方提倡的在使用wait  和notifyAll的時候,必須使用while循環條件判斷。blog

相關文章
相關標籤/搜索