java 線程並發(生產者、消費者模式)


線程並發協作(生產者/消費者模式)

多線程環境下,我們經常需要多個線程的並發和協作。這個時候,就需要了解一個重要的多線程並發協作模型“生產者/消費者模式”。

Ø 什么是生產者?

      生產者指的是負責生產數據的模塊(這里模塊可能是:方法、對象、線程、進程)。

Ø 什么是消費者?

      消費者指的是負責處理數據的模塊(這里模塊可能是:方法、對象、線程、進程)。

Ø 什么是緩沖區?

      消費者不能直接使用生產者的數據,它們之間有個“緩沖區”。生產者將生產好的數據放入“緩沖區”,消費者從“緩沖區”拿要處理的數據。

 

 

 緩沖區是實現並發的核心,緩沖區的設置有3個好處:

Ø 實現線程的並發協作

      有了緩沖區以后,生產者線程只需要往緩沖區里面放置數據,而不需要管消費者消費的情況;同樣,消費者只需要從緩沖區拿數據處理即可,也不需要管生產者生產的情況。 這樣,就從邏輯上實現了“生產者線程”和“消費者線程”的分離。

Ø 解耦了生產者和消費者

      生產者不需要和消費者直接打交道。

Ø 解決忙閑不均,提高效率

      生產者生產數據慢時,緩沖區仍有數據,不影響消費者消費;消費者處理數據慢時,生產者仍然可以繼續往緩沖區里面放置數據 。

生產者與消費者模式

public class TestProduce {
    public static void main(String[] args) {
        SyncStack sStack = new SyncStack();// 定義緩沖區對象;
        Shengchan sc = new Shengchan(sStack);// 定義生產線程;
        Xiaofei xf = new Xiaofei(sStack);// 定義消費線程;
        sc.start();
        xf.start();
    }
}
 
class Mantou {// 饅頭
    int id;
 
    Mantou(int id) {
        this.id = id;
    }
}
 
class SyncStack {// 緩沖區(相當於:饅頭筐)
    int index = 0;
    Mantou[] ms = new Mantou[10];
 
    public synchronized void push(Mantou m) {
        while (index == ms.length) {//說明饅頭筐滿了
            try {
               //wait后,線程會將持有的鎖釋放,進入阻塞狀態;
               //這樣其它需要鎖的線程就可以獲得鎖;
                this.wait();
                //這里的含義是執行此方法的線程暫停,進入阻塞狀態,
                //等消費者消費了饅頭后再生產。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 喚醒在當前對象等待池中等待的第一個線程。
        //notifyAll叫醒所有在當前對象等待池中等待的所有線程。
        this.notify();
        // 如果不喚醒的話。以后這兩個線程都會進入等待線程,沒有人喚醒。
        ms[index] = m;
        index++;
    }
 
    public synchronized Mantou pop() {
        while (index == 0) {//如果饅頭筐是空的;
            try {
                //如果饅頭筐是空的,就暫停此消費線程(因為沒什么可消費的嘛)。
                this.wait();                //等生產線程生產完再來消費;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.notify();
        index--;
        return ms[index];
    }
}
 
class Shengchan extends Thread {// 生產者線程
    SyncStack ss = null;
 
    public Shengchan(SyncStack ss) {
        this.ss = ss;
    }
 
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("生產饅頭:" + i);
            Mantou m = new Mantou(i);
            ss.push(m);
        }
    }
}
 
class Xiaofei extends Thread {// 消費者線程;
    SyncStack ss = null;
 
    public Xiaofei(SyncStack ss) {
        this.ss = ss;
    }
 
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            Mantou m = ss.pop();
            System.out.println("消費饅頭:" + i);
 
        }
    }
}

執行結果如圖:

 

 

線程並發協作總結:

      線程並發協作(也叫線程通信),通常用於生產者/消費者模式,情景如下:

      1. 生產者和消費者共享同一個資源,並且生產者和消費者之間相互依賴,互為條件。

      2. 對於生產者,沒有生產產品之前,消費者要進入等待狀態。而生產了產品之后,又需要馬上通知消費者消費。

      3. 對於消費者,在消費之后,要通知生產者已經消費結束,需要繼續生產新產品以供消費。

      4. 在生產者消費者問題中,僅有synchronized是不夠的。

        · synchronized可阻止並發更新同一個共享資源,實現了同步;

        · synchronized不能用來實現不同線程之間的消息傳遞(通信)。

      5. 那線程是通過哪些方法來進行消息傳遞(通信)的呢?見如下總結:

 

6. 以上方法均是java.lang.Object類的方法;

      都只能在同步方法或者同步代碼塊中使用,否則會拋出異常。

 

在實際開發中,尤其是“架構設計”中,會大量使用這個模式。 對於初學者了解即可,如果晉升到中高級開發人員,這就是必須掌握的內容。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM