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

Java Condition 精準控制鎖機制:await/signal 替代 wait/notify 完整實戰

     編輯:Java 相關知識

Condition 核心優勢

java.util.concurrent.locks.Condition 將 Object 監視器方法(wait/notify/notifyAll)分離為獨立物件,配合 Lock 實現:

  • 單一鎖關聯多個 Condition(多等待隊列)
  • 精準喚醒特定條件的執行緒
  • 避免虛假喚醒導致的效能浪費

API 對照表

Object 方法Condition 方法說明
wait()await()釋放鎖並阻塞
wait(timeout)await(time, unit)超時阻塞
notify()signal()喚醒單一執行緒
notifyAll()signalAll()喚醒所有執行緒

基礎範例:生產者-消費者模式

class BoundedBuffer {
    final Lock lock = new ReentrantLock();
    final Condition notFull = lock.newCondition();  // 寫入等待條件
    final Condition notEmpty = lock.newCondition(); // 讀取等待條件
    
    final Object[] items = new Object[100];
    int putPtr = 0, takePtr = 0, count = 0;
    
    public void put(Object x) throws InterruptedException {
        lock.lock();
        try {
            while (count == items.length) {
                notFull.await(); // 隊列滿,阻塞寫入執行緒
            }
            items[putPtr] = x;
            if (++putPtr == items.length) putPtr = 0;
            ++count;
            notEmpty.signal(); // 喚醒一個讀取執行緒
        } finally {
            lock.unlock();
        }
    }
    
    public Object take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) {
                notEmpty.await(); // 隊列空,阻塞讀取執行緒
            }
            Object x = items[takePtr];
            if (++takePtr == items.length) takePtr = 0;
            --count;
            notFull.signal(); // 喚醒一個寫入執行緒
            return x;
        } finally {
            lock.unlock();
        }
    }
}

傳統 wait/notify 版本對比

class TraditionalBuffer {
    final Object[] items = new Object[100];
    int putPtr = 0, takePtr = 0, count = 0;
    
    public synchronized void put(Object x) throws InterruptedException {
        while (count == items.length) wait(); // 單一等待隊列
        items[putPtr] = x;
        if (++putPtr == items.length) putPtr = 0;
        ++count;
        notifyAll(); // 唤醒所有,含不必要的執行緒
    }
    
    public synchronized Object take() throws InterruptedException {
        while (count == 0) wait();
        Object x = items[takePtr];
        if (++takePtr == items.length) takePtr = 0;
        --count;
        notifyAll(); // 同樣喚醒所有
        return x;
    }
}

關鍵差異分析

場景Conditionwait/notify
隊列滿時僅阻塞寫入執行緒 (notFull)阻塞所有執行緒
隊列空時僅阻塞讀取執行緒 (notEmpty)阻塞所有執行緒
喚醒策略精準 signal 特定隊列notifyAll 驚群效應
鎖綁定顯式 Lock 物件隱式 this 監視器
多條件支援原生支援多 Condition需額外物件協調

進階應用:多階段管線處理

class Pipeline {
    final Lock lock = new ReentrantLock();
    final Condition stage1Done = lock.newCondition();
    final Condition stage2Done = lock.newCondition();
    final Condition stage3Done = lock.newCondition();
    
    enum Stage { STAGE1, STAGE2, STAGE3, DONE }
    Stage currentStage = Stage.STAGE1;
    
    public void executeStage1() throws InterruptedException {
        lock.lock();
        try {
            while (currentStage != Stage.STAGE1) stage1Done.await();
            // 執行階段 1 邏輯
            currentStage = Stage.STAGE2;
            stage2Done.signal();
        } finally { lock.unlock(); }
    }
    
    public void executeStage2() throws InterruptedException {
        lock.lock();
        try {
            while (currentStage != Stage.STAGE2) stage2Done.await();
            // 執行階段 2 邏輯
            currentStage = Stage.STAGE3;
            stage3Done.signal();
        } finally { lock.unlock(); }
    }
    
    public void executeStage3() throws InterruptedException {
        lock.lock();
        try {
            while (currentStage != Stage.STAGE3) stage3Done.await();
            // 執行階段 3 邏輯
            currentStage = Stage.DONE;
        } finally { lock.unlock(); }
    }
}

最佳實踐原則

  1. 必須在 Lock 保護下呼叫:await/signal 前需持有鎖
  2. 使用 while 迴圈檢查條件:防範虛假喚醒(雖 Condition 較少見)
  3. 釋放鎖用 finally 區塊:確保異常時也能釋放
  4. 優先 signal 而非 signalAll:減少不必要的執行緒競爭
  5. 命名語意化:notEmpty/notFull 比 cond1/cond2 更易維護

常見錯誤避坑

錯誤後果修正
未持有鎖呼叫 await()IllegalMonitorStateException確保 lock.lock() 後再呼叫
使用 if 而非 while 檢查條件虛假喚醒導致邏輯錯誤改用 while 迴圈
signal 後未釋放鎖被喚醒執行緒無法取得鎖在 finally 中 unlock
Condition 物件跨鎖使用行為未定義每個 Condition 專屬單一 Lock
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved