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

Java 線程核心機制(五):等待-通知模式深度解析與 Condition 進階

     編輯:Java 相關知識

早期嘗試用 volatile boolean done 標記控制線程結束,雖可行但會導致 CPU 空轉(忙等待),浪費資源。Java 物件內建的 wait()/notify() 機制提供了高效的線程協作方案。

等待-通知機制本質

  • 同步機制:需在 synchronized 方法/塊中調用
  • 通信機制:線程間就「特定條件」進行信號傳遞
  • 非替代品:不能替代 synchronized 解決競態條件,兩者配合使用

經典範例:動畫線程控制

private boolean done = true; // 無需 volatile,因同步塊保護

public synchronized void run() {
    while (true) {
        try {
            if (done) {
                wait(); // 條件不滿足,釋放鎖進入等待
            } else {
                repaint();
                wait(100); // 定時喚醒重繪
            }
        } catch (InterruptedException e) { return; }
    }
}

public synchronized void setDone(boolean b) {
    done = b;
    if (timer == null) { timer = new Thread(this); timer.start(); }
    if (!done) notify(); // 條件滿足,通知等待線程
}

關鍵細節深度剖析

為何用 wait() 而非 sleep()?

  • wait() 釋放同步鎖,允許其他線程進入同步塊修改條件並調用 notify()
  • sleep() 持有鎖不釋放 → setDone() 永無法獲取鎖 → 死鎖

run() 同步化的安全性

雖 run() 無限循環,但 wait() 釋放鎖,避免了「鎖永不釋放」的風險。

通知遺失問題

若 notify() 時無線程等待,通知丟失。後續 wait() 必須等待下一次通知 → 必須在循環中檢查條件。

競態條件與同步鎖

「檢查條件 → 調用 wait()」與「設置條件 → 調用 notify()」之間存在競態,必須在同一把鎖保護下完成,確保原子性。

虛假喚醒與多線程競爭

  • 線程從 wait() 返回不保證條件成立,必須重新檢查條件(循環模式)
  • 多線程等待同一條件時,notify() 隨機喚醒一個,其他線程需重新等待
  • 若需喚醒所有等待線程,使用 notifyAll()(實際仍受鎖限制,同一時刻僅一線程執行)

獨立鎖對象模式:降低鎖競爭

private Object doneLock = new Object();

public void run() {
    synchronized (doneLock) {
        while (true) {
            if (done) doneLock.wait();
            else { repaint(); doneLock.wait(100); }
        }
    }
}

public void setDone(boolean b) {
    synchronized (doneLock) {
        done = b;
        if (!done) doneLock.notify();
    }
}

優勢:將「條件鎖」與「物件鎖」分離,提高並發吞吐。

JDK 5+ Condition 介面:更強大的等待-通知

private Lock lock = new ReentrantLock();
private Condition cv = lock.newCondition();

public void run() {
    lock.lock();
    try {
        while (true) {
            if (done) cv.await();
            else { nextCharacter(); cv.await(getPauseTime(), TimeUnit.MILLISECONDS); }
        }
    } catch (InterruptedException e) { return; }
    finally { lock.unlock(); }
}

public void setDone(boolean b) {
    lock.lock();
    try {
        done = b;
        if (!done) cv.signal();
    } finally { lock.unlock(); }
}

Condition 核心優勢

  1. 綁定 Lock:Lock 內部已佔用 wait/notify,不可混用
  2. 多條件變量:單一 Lock 可創建多個 Condition,實現精準喚醒(如「非空」與「非滿」分離)
  3. 豐富 API:awaitUntil()、awaitNanos() 等支援絕對/相對超時,可中斷等待

本質上 Condition 復刻了 wait/notify 語義,但提供了更靈活的控制粒度與超時語義。

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