多執行緒——虛假喚醒
一、虛假喚醒示例
public class TestProducerAndCustomer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer = new Producer(clerk);
Consumer consumer = new Consumer(clerk);
Producer producer2 = new Producer(clerk);
Consumer consumer2 = new Consumer(clerk);
new Thread(producer, "生產者A").start();
new Thread(consumer, "消費者B").start();
new Thread(producer2, "生產者C").start();
new Thread(consumer2, "消費者D").start();
}
}
// 店員類:負責進貨和售貨
class Clerk {
private int num = 0; //店裡當前的貨物量
public synchronized void get() { //店員進貨 每次進貨一個(生產者)
if (num >= 1) {
System.out.println(Thread.currentThread().getName() + " 庫存已滿,無法進貨");
try {
this.wait();
System.out.println(Thread.currentThread().getName() + " wait後剩餘步驟");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " : " + (++num));
this.notifyAll();
}
public synchronized void sale() { //店員賣貨 每次賣掉一個貨(消費者)
if (num <= 0) {
System.out.println(Thread.currentThread().getName() + " 庫存已空,無法賣貨");
try {
this.wait();
System.out.println(Thread.currentThread().getName() + " wait後剩餘步驟");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " : " + (--num));
this.notifyAll();
}
}
// 生產者 可以有很多生產者賣貨給這個店員
class Producer implements Runnable {
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.get();
}
}
}
//消費者:可以很多消費者找店員買貨
class Consumer implements Runnable {
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.sale();
}
}
}
執行結果如下:
現在我們來分析一下這個過程
TIME | 發生操作 | 倉庫數量 |
---|---|---|
TIME1 | 生產者A拿到鎖,進行生產 | 1 |
TIME2 | 生產者A再次拿到鎖,因為倉庫已滿,A等待 | 1 |
TIME3 | 生產者C拿到鎖,倉庫已滿,C也進入等待 | 1 |
TIME4 | 消費者D拿到鎖,進行消費,同時喚醒等待中的佇列(即下一輪鎖資源,將由A、B、C、D一起搶佔) | 0 |
TIME5 | 消費者D拿到鎖,倉庫為空,D等待;A、B、C搶佔鎖資源 | 0 |
TIME6 | 消費者B拿到鎖,倉庫為空,B等待 | 0 |
TIME7 | 生產者C拿到鎖,因為C是從等待中被喚醒的,所以從wait之後開始執行,進行生產 ,並喚醒B、D | 1 |
TIME8 | 生產者C再次拿到鎖,倉庫已滿,C進入等待 | 1 |
TIME9 | 生產者A拿到鎖,因為A是從等待中被喚醒的,所以從wait之後執行,進行生產,並喚醒C | 2(發生虛假喚醒) |
為什麼會產生這種原因呢?因為我們在進行是否wait是用的是if迴圈,if語句只執行一次,當等待中的鎖被喚醒,它將直接繼續向下執行,如果使用while,就會迴圈判斷是否滿足條件,如果不滿足將繼續進入等待。
二、正確開啟方式
程式碼如下:
public class TestProducerAndCustomer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer = new Producer(clerk);
Consumer consumer = new Consumer(clerk);
Producer producer2 = new Producer(clerk);
Consumer consumer2 = new Consumer(clerk);
new Thread(producer, "生產者A").start();
new Thread(consumer, "消費者B").start();
new Thread(producer2, "生產者C").start();
new Thread(consumer2, "消費者D").start();
}
}
// 店員類:負責進貨和售貨
class Clerk {
private int num = 0; //店裡當前的貨物量
public synchronized void get() { //店員進貨 每次進貨一個(生產者)
while (num >= 1) {
System.out.println(Thread.currentThread().getName() + " 庫存已滿,無法進貨");
try {
this.wait();
System.out.println(Thread.currentThread().getName() + " wait後剩餘步驟");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " : " + (++num));
this.notifyAll();
}
public synchronized void sale() { //店員賣貨 每次賣掉一個貨(消費者)
while (num <= 0) {
System.out.println(Thread.currentThread().getName() + " 庫存已空,無法賣貨");
try {
this.wait();
System.out.println(Thread.currentThread().getName() + " wait後剩餘步驟");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " : " + (--num));
this.notifyAll();
}
}
// 生產者 可以有很多生產者賣貨給這個店員
class Producer implements Runnable {
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.get();
}
}
}
//消費者:可以很多消費者找店員買貨
class Consumer implements Runnable {
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.sale();
}
}
}
總結
在進行wait操作的條件判斷時,注意判斷語句的選擇,以防出現虛假喚醒。
Good Good Study,Day Day Up!
相關文章
- 執行緒虛假喚醒問題剖析執行緒
- java多執行緒之消費生產模型-使用synchronized解決虛假喚醒Java執行緒模型synchronized
- Java 多執行緒基礎(六)執行緒等待與喚醒Java執行緒
- 工作執行緒的喚醒及建立(19)執行緒
- 每個鎖建立多個條件佇列以避免虛假喚醒佇列
- 多執行緒-生產者消費者之等待喚醒機制執行緒
- 條件變數的虛假喚醒(spurious wakeups)問題變數
- 從 JDK 原始碼角度看執行緒的阻塞和喚醒JDK原始碼執行緒
- 執行緒等待兩種方法的喚醒的效率比較執行緒
- 2020119-多執行緒暫停和喚醒執行緒
- 多執行緒-生產者消費者之等待喚醒機制程式碼優化執行緒優化
- 破除java神話之執行緒按優先順序喚醒Java執行緒
- dubbo傳送過程編碼失敗,會喚醒傳送執行緒嗎?執行緒
- 多執行緒【執行緒池】執行緒
- 多執行緒--執行緒管理執行緒
- Java多執行緒——執行緒Java執行緒
- VC多執行緒 C++ 多執行緒執行緒C++
- 多執行緒-執行緒控制之休眠執行緒執行緒
- 多執行緒-執行緒控制之加入執行緒執行緒
- 多執行緒-執行緒控制之禮讓執行緒執行緒
- 多執行緒-執行緒控制之中斷執行緒執行緒
- Java併發包原始碼學習系列:掛起與喚醒執行緒LockSupport工具類Java原始碼執行緒
- 多執行緒之初識執行緒執行緒
- Java多執行緒-執行緒中止Java執行緒
- Java多執行緒——執行緒池Java執行緒
- 多執行緒-執行緒概述等執行緒
- 多執行緒系列(1),多執行緒基礎執行緒
- 多執行緒系列(二):多執行緒基礎執行緒
- 多執行緒------執行緒與程式/執行緒排程/建立執行緒執行緒
- 多執行緒-執行緒控制之守護執行緒執行緒
- a、多執行緒執行緒
- java 多執行緒守護執行緒Java執行緒
- Java多執行緒-執行緒通訊Java執行緒
- Java多執行緒-執行緒狀態Java執行緒
- Java多執行緒(2)執行緒鎖Java執行緒
- java多執行緒9:執行緒池Java執行緒
- Java多執行緒之執行緒中止Java執行緒
- 多執行緒系列之 執行緒安全執行緒