線程並發協作(生產者/消費者模式)
多線程環境下,我們經常需要多個線程的並發和協作。這個時候,就需要了解一個重要的多線程並發協作模型“生產者/消費者模式”。
Ø 什么是生產者?
生產者指的是負責生產數據的模塊(這里模塊可能是:方法、對象、線程、進程)。
Ø 什么是消費者?
消費者指的是負責處理數據的模塊(這里模塊可能是:方法、對象、線程、進程)。
Ø 什么是緩沖區?
消費者不能直接使用生產者的數據,它們之間有個“緩沖區”。生產者將生產好的數據放入“緩沖區”,消費者從“緩沖區”拿要處理的數據。
緩沖區是實現並發的核心,緩沖區的設置有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類的方法;
都只能在同步方法或者同步代碼塊中使用,否則會拋出異常。
在實際開發中,尤其是“架構設計”中,會大量使用這個模式。 對於初學者了解即可,如果晉升到中高級開發人員,這就是必須掌握的內容。