首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 多線程生產者消費者模式:從雙線程到多線程的完美演進

     編輯:Java 綜合教學

Java 多線程生產者消費者模式:從雙線程到多線程的完美演進

生產者-消費者是經典的並發協作模式。本文通過「單生產單消費」→「多生產多消費」的層層遞進,揭示 wait/notify 陷阱,最終給出生產級 while + notifyAll 正確實現。

場景一:一生產一消費

共享資源類 Resource 使用 boolean flag 標記是否有產品,synchronized 保證原子性,wait() 釋放鎖等待,notify() 喚醒對方。

public class Resource {
    private int number = 0;
    private boolean flag = false;

    public synchronized void create() {
        if (flag) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } }
        number++;
        System.out.println(Thread.currentThread().getName() + "生產者------------" + number);
        flag = true;
        notify();
    }

    public synchronized void destroy() {
        if (!flag) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } }
        System.out.println(Thread.currentThread().getName() + "消費者****" + number);
        flag = false;
        notify();
    }
}

測試類啟動一對線程,輸出完美交替:生產1 → 消費1 → 生產2 → 消費2 …

場景二:多生產多消費(雙線程各兩個)

直接啟動 2 個 Producer + 2 個 Consumer,出現嚴重問題:

  • 重複消費:同一產品被消費兩次(如 101)。
  • 遺漏消費:產品生產後無人消費(如 105)。

根因分析

  1. if 判斷只檢查一次,被 notify() 喚醒後不再複查條件,導致「虛假喚醒」繼續執行。
  2. notify() 隨機喚醒單個線程,可能喚醒同類線程(生產喚醒生產),造成死鎖或邏輯錯誤。

修正步驟 1:if → while

將條件判斷改為循環,喚醒後重新檢查:

while (flag) { wait(); }  // 生產者
while (!flag) { wait(); } // 消費者

解決虛假喚醒,但引發新問題:程序運行到某值(如 74)卡死——死鎖。原因:notify() 可能喚醒同類,導致雙方都在等待對方。

修正步驟 2:notify → notifyAll

喚醒所有等待線程,確保對方類型線程一定被喚醒:

notifyAll(); // 生產與消費雙方均改用 notifyAll

最終正確代碼

public class Resource {
    private int number = 0;
    private boolean flag = false;

    public synchronized void create() {
        while (flag) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } }
        number++;
        System.out.println(Thread.currentThread().getName() + "生產者------------" + number);
        flag = true;
        notifyAll();
    }

    public synchronized void destroy() {
        while (!flag) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } }
        System.out.println(Thread.currentThread().getName() + "消費者****" + number);
        flag = false;
        notifyAll();
    }
}

運行結果:多線程下嚴格交替,無重複、無遺漏、無死鎖。

核心經驗總結

  1. 永遠使用 while 而非 if 檢查等待條件,防止虛假喚醒。
  2. 多生產/多消費場景必須用 notifyAll(),避免同類線程互喚導致死鎖。
  3. 生產環境建議直接使用 java.util.concurrent 包下的 BlockingQueue(如 ArrayBlockingQueue),更簡潔高效。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved