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

Java Lock 與 Condition 實作生產者-消費者模式:取代 wait/notify 的現代並發寫法

     編輯:Java 相關知識

顯式鎖 + 多條件變量:更靈活、更可讀的線程協作

synchronized + wait/notify 是舊世代協作方式,缺點:單一條件隊列、無法中斷等待、無公平鎖選擇。ReentrantLock + Condition 提供:多條件變量(notFull/notEmpty)、可中斷等待、公平/非公平鎖、等待超時,是生產者-消費者的標準現代實作。

核心結構

class BoundedBuffer {
    private final List<Object> buffer = new ArrayList<>();
    private final int capacity;
    private final Lock lock = new ReentrantLock(true); // 公平鎖
    private final Condition notFull  = lock.newCondition(); // 生產者等待
    private final Condition notEmpty = lock.newCondition(); // 消費者等待

    public void produce() {
        lock.lock();
        try {
            while (buffer.size() == capacity) notFull.await(); // 滿了等待
            buffer.add(new Object());
            notEmpty.signalAll(); // 喚醒消費者
        } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
        finally { lock.unlock(); }
    }

    public void consume() {
        lock.lock();
        try {
            while (buffer.isEmpty()) notEmpty.await(); // 空了等待
            buffer.remove(0);
            notFull.signalAll(); // 喚醒生產者
        } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
        finally { lock.unlock(); }
    }
}

關鍵優勢對比

特性synchronized + wait/notifyLock + Condition
條件隊列單一(物件鎖綁定)多個(newCondition())
等待可中斷不可(只能 notify)await() 拋 InterruptedException
公平鎖不支援new ReentrantLock(true)
嘗試獲取鎖不支援tryLock(timeout)
等待超時wait(timeout) 易誤用await(timeout, TimeUnit) 精確
程式碼結構同步塊分散鎖/條件物件化,職責清晰

最佳實踐檢查清單

  1. while 而非 if:while (condition) await() 防止虛假喚醒。
  2. finally 釋放鎖:lock.unlock() 必須在 finally,否則死鎖。
  3. 中斷處理:捕獲 InterruptedException 後重設中斷旗標 Thread.currentThread().interrupt(),讓上層感知。
  4. signalAll vs signal:多生產/多消費場景用 signalAll() 避免信號遺漏;單生產單消費可用 signal() 提升效能。
  5. 容量界限:有界緩衝區防止 OOM,無界請用 LinkedBlockingQueue 直接替代手寫。

生產級替代:直接用 JUC 阻塞隊列

BlockingQueue<Task> queue = new ArrayBlockingQueue<>(capacity);
// 生產者:queue.put(task); // 滿阻塞
// 消費者:Task t = queue.take(); // 空阻塞

除非教學、極度定制化需求,優先使用 BlockingQueue 實作,經過嚴格測試、效能優化、功能完整(剩餘容量、優先級、遲緩提交等)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved