多執行緒——虛假喚醒
一、虛假喚醒示例
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執行緒
- 2020119-多執行緒暫停和喚醒執行緒
- 工作執行緒的喚醒及建立(19)執行緒
- 每個鎖建立多個條件佇列以避免虛假喚醒佇列
- 多執行緒和多執行緒同步執行緒
- dubbo傳送過程編碼失敗,會喚醒傳送執行緒嗎?執行緒
- 執行緒與多執行緒執行緒
- 多執行緒【執行緒池】執行緒
- 多執行緒--執行緒管理執行緒
- Java多執行緒-執行緒中止Java執行緒
- 多執行緒之初識執行緒執行緒
- 多執行緒------執行緒與程式/執行緒排程/建立執行緒執行緒
- 多執行緒系列(1),多執行緒基礎執行緒
- a、多執行緒執行緒
- 多執行緒之手撕執行緒池執行緒
- Java多執行緒-執行緒狀態Java執行緒
- Java多執行緒-執行緒通訊Java執行緒
- kuangshenshuo-多執行緒-執行緒池執行緒
- java多執行緒9:執行緒池Java執行緒
- java 多執行緒守護執行緒Java執行緒
- Java多執行緒(2)執行緒鎖Java執行緒
- 【java多執行緒】(二)執行緒停止Java執行緒
- 多執行緒系列之 執行緒安全執行緒
- iOS 多執行緒之執行緒安全iOS執行緒
- Java多執行緒之執行緒中止Java執行緒
- Android多執行緒之執行緒池Android執行緒
- Java併發包原始碼學習系列:掛起與喚醒執行緒LockSupport工具類Java原始碼執行緒
- 6、JUC:傳統的生產者消費者問題,防止虛假喚醒問題
- 執行緒以及多執行緒,多程式的選擇執行緒
- 多執行緒學習一(多執行緒基礎)執行緒
- Java多執行緒(一)多執行緒入門篇Java執行緒
- 剖析虛幻渲染體系(02)- 多執行緒渲染執行緒
- 多執行緒,多程式執行緒
- 【多執行緒總結(二)-執行緒安全與執行緒同步】執行緒
- 【Java多執行緒】輕鬆搞定Java多執行緒(二)Java執行緒