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

Java 多線程設計模式:wait/notify 協作機制深度解析

     編輯:Java 相關知識

場景引入:線程間的協作與等待

在多線程開發中,線程間往往需要協調工作。典型場景:瀏覽器中,顯示線程 需等待 下載線程 完成圖片下載才能渲染。若圖片未就緒,顯示線程應暫停(等待);下載完成後,下載線程通知顯示線程「可以顯示了」,顯示線程恢復執行。

這邏輯用 pseudocode 表示:

// 等待方
if (條件不滿足) {
    等待();
}
// 繼續執行

// 通知方
條件 = true;
通知等待線程();

Java 中這一機制的核心是 Object.wait() 與 Object.notify()/notifyAll(),且必須配合鎖(synchronized)使用。

標準範式:Guarded Suspension 模式

synchronized(obj) {
    while (!condition) { // 必須用 while 防止虛假喚醒
        obj.wait();
    }
    obj.doSomething(); // 條件滿足,執行業務邏輯
}

執行流程:

  1. 線程 A 獲得 obj 鎖,檢查條件不滿足,調用 obj.wait() 釋放鎖並進入等待隊列。
  2. 線程 B 獲得 obj 鎖,修改條件使其滿足,調用 obj.notify() 喚醒一個等待線程(或 notifyAll() 喚醒全部)。
  3. 線程 A 被喚醒,但必須重新獲得 obj 鎖 才能從 wait() 返回繼續執行。

五大核心注意點(面試高頻考點)

1. 必須在 synchronized 代碼塊中調用

wait()、notify()、notifyAll() 均為 Object 實例方法,調用前必須持有該對象的鎖,否則拋出 IllegalMonitorStateException。

2. wait() 會釋放鎖,notify() 不會立即放鎖

  • wait():線程釋放鎖,進入 WAITING 狀態。
  • notify():線程不釋放鎖,只是將目標線程從等待隊列移至同步隊列(BLOCKED 狀態),等待鎖釋放。
  • 只有持鎖線程退出 synchronized 塊,鎖才真正釋放,被喚醒線程才有機會獲鎖繼續。

3. 喚醒後需重新獲鎖

wait() 返回時,線程已重新獲得鎖。這意味著喚醒與繼續執行之間存在時間差,條件可能再次發生變化,因此必須用 while 循環檢查條件,不能用 if。

4. notify() 喚醒單個線程,notifyAll() 喚醒全部

  • notify():JVM 隨機選擇一個等待線程喚醒(適合「單生產者單消費者」或等待條件相同的場景)。
  • notifyAll():喚醒所有等待線程,它們競爭鎖,獲得鎖的繼續執行,其餘重新進入 BLOCKED 狀態(適合條件不同、需重新檢查的場景)。

5. 通知發生時,通知者仍持有鎖

被喚醒線程雖從 wait() 返回,但無法立即獲鎖,必須等通知者退出 synchronized 塊。這保證了條件修改的原子可見性。

代碼示範:生產者-消費者模型

public class SharedBuffer {
    private final List buffer = new ArrayList<>();
    private final int capacity = 10;

    public synchronized void produce(int value) throws InterruptedException {
        while (buffer.size() == capacity) {
            wait(); // 緩衝區滿,等待消費
        }
        buffer.add(value);
        System.out.println("Produced: " + value + ", Size: " + buffer.size());
        notifyAll(); // 通知消費者
    }

    public synchronized int consume() throws InterruptedException {
        while (buffer.isEmpty()) {
            wait(); // 緩衝區空,等待生產
        }
        int value = buffer.remove(0);
        System.out.println("Consumed: " + value + ", Size: " + buffer.size());
        notifyAll(); // 通知生產者
        return value;
    }
}

最佳實踐總結

  1. 永遠使用 while 循環檢查條件,防止虛假喚醒與條件變化。
  2. 優先使用 notifyAll(),除非確定所有等待線程條件完全一致且只需喚醒一個。
  3. 盡量縮小同步塊範圍,減少鎖持有時間,提高併發度。
  4. 考慮使用 java.util.concurrent 封裝類(如 Condition, BlockingQueue),避免手動管理 wait/notify 的易錯細節。

掌握 wait/notify 協作機制,是編寫正確、高效多線程程序的基石。實戰中建議優先使用高層並發工具,但在理解底層原理、排查死鎖/活鎖時,這些知識不可或缺。

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