多線程之生產者消費者問題解析

在多線程系統中,不同的線程執行不同的任務;如果這些任務之間存在聯繫,那麼執行這些任務的線程之間就必須能夠通信,共同協調完成系統任務。這就產生了生產者和消費者的問題

簡單例子

新建資源類

class bed{

    private int number=0;

    public synchronized void increment()throws Exception{
        // 1.判斷
        if (number!=0){
            wait();
        }
        // 2.幹活
        number++;
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        // 3.通知
        this.notifyAll();
    }

    public synchronized void decrement()throws Exception{
        // 1.判斷
        if (number==0){
            wait();
        }
        // 2.幹活
        number--;
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        // 3.通知
        this.notifyAll();

    }
}

新建執行類

public class ProdConsumerDemo04 {
    public static void main(String[] args) {
        Bed bed=new Bed ();

        new Thread(()->{
            for (int i = 0; i <=10 ; i++) {
                try {
                    bed.increment();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"A").start();

        new Thread(()->{
            for (int i = 0; i <=10 ; i++) {
                try {
                    bed.decrement();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}

執行結果
在這裏插入圖片描述
從上圖看,目前線程生產一個消費一個,結果是正確的,但是當線程增加到4個,加上sleep方法模擬卡頓時,如下圖
在這裏插入圖片描述
此時執行結果就出現了問題,不再是生產一個消費一個
在這裏插入圖片描述

分析

這是爲什麼呢?首先明確幾點
1.synchronized 在線程wait()時將會暫時失去加鎖效果
2.

假設此時有一個工廠,外面有4個工人,兩個負責生產,兩個負責搬運,工廠一次只允許進入一個工人生產,而且如果工人進入時發現有產品,就暫時等待,不生產產品,並且通知工人進來,我們把4個工人分別命名爲a1,a2.b1,b2

下圖中,a1工人先進入工廠,生產了1個產品,此時a1通知外面可以進來一個工人了,正常情況下應該進入一個搬運工人b1,或者b2
在這裏插入圖片描述

但是此時a2搶到了線程,但是由於產品未被消費,第二個工人仍在等待,同時通知可以進來了,此時外面有3個人a1,b1,c1,有一種可能a1又進來了,此時工廠中有兩個生產的工人,a1進來時有產品,所以a1也等待了,同時通知外面,可以再進來人
在這裏插入圖片描述
工廠中此時有三個人,b1進來之後消費了一個產品,同時喚醒所有工人,但是由於a1,a2都在此等待,而且都通過了有是否有產品的判斷,他不會再判斷一回,直接開始生產,所以此時產品的數量爲2
在這裏插入圖片描述
這是因爲出現了虛假喚醒,什麼是虛假喚醒呢?

爲防止虛假喚醒,應該將資源類的if判斷換爲while,JDK1.8官網上給出如下解釋

像在一個參數版本中,中斷和虛假喚醒是可能的,並且該方法應該始終在循環中使用:

  synchronized (obj) {
         while (<condition does not hold>)
             obj.wait();
         ... // Perform action appropriate to condition
     } 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章