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

JavaSE 線程通信詳解:wait/notify/notifyAll 同步協作模式、生產者消費者實戰

     編輯:Java 相關知識

線程通信是多線程協作的核心,wait()/notify()/notifyAll() 是 Object 級別的同步協作原語。本文深度解析三大方法語義、同步鎖綁定規則、等待喚醒流程、虛假喚醒防範、經典生產者消費者模式實現,並對比 Condition 高級用法。

核心三大方法:語義與約束

關鍵規則:同一把鎖

wait/notify 必須在同一個對象鎖上調用。線程 A synchronized(obj) { obj.wait(); },線程 B synchronized(obj) { obj.notify(); }。鎖對象不一致 → IllegalMonitorStateException。

等待-喚醒流程圖解

線程 A (消費者)                    線程 B (生產者)
synchronized(obj) {               synchronized(obj) {
    while (queue.empty()) {           produce();
        obj.wait();        ──────→    obj.notify();  // 唤醒 A
    }                                 }
    consume();                        // B 退出 synchronized 釋放鎖
}                                     // A 從入口集獲取鎖,繼續執行

經典生產者消費者:有界隊列實現

class BoundedBuffer<T> {
    private final List<T> buffer = new ArrayList<>();
    private final int capacity;
    
    public BoundedBuffer(int capacity) { this.capacity = capacity; }
    
    // 生產者調用
    public synchronized void put(T item) throws InterruptedException {
        while (buffer.size() == capacity) { // while 防虛假喚醒
            wait(); // 釋放鎖,等待空槽
        }
        buffer.add(item);
        notifyAll(); // 唤醒所有消費者
    }
    
    // 消費者調用
    public synchronized T take() throws InterruptedException {
        while (buffer.isEmpty()) {
            wait(); // 釋放鎖,等待數據
        }
        T item = buffer.remove(0);
        notifyAll(); // 唤醒所有生產者
        return item;
    }
}

關鍵細節

  1. while 而非 if:防止虛假喚醒(Spurious Wakeup)——線程在無 notify 情況下被喚醒,JVM 允許。while (condition) wait() 確保條件真正滿足才繼續。
  2. notifyAll 優於 notify:多生產多消費場景,notify 可能喚醒同類線程(如喚醒另一生產者),導致無效切換;notifyAll 喚醒所有,由線程自行檢查條件。
  3. 同步方法隱式鎖:synchronized 方法鎖住 this,等價於 synchronized(this)。

進階:Lock + Condition 替代 Object 監視器

JDK 5+ java.util.concurrent.locks.Condition 提供更強大的等待/通知機制:

class BoundedBufferV2<T> {
    private final Lock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();
    private final List<T> buffer = new ArrayList<>();
    private final int capacity;
    
    public void put(T item) throws InterruptedException {
        lock.lock();
        try {
            while (buffer.size() == capacity) notFull.await();
            buffer.add(item);
            notEmpty.signal(); // 精確喚醒消費者
        } finally { lock.unlock(); }
    }
    
    public T take() throws InterruptedException {
        lock.lock();
        try {
            while (buffer.isEmpty()) notEmpty.await();
            T item = buffer.remove(0);
            notFull.signal(); // 精確喚醒生產者
            return item;
        } finally { lock.unlock(); }
    }
}

Condition 優勢

  • 一把鎖綁定多個 Condition(notFull/notEmpty),精確喚醒目標線程組,避免無效競爭。
  • 支持 awaitUninterruptibly()、awaitNanos()、awaitUntil() 多種等待模式。
  • 可中斷/不可中斷靈活控制。

實戰避坑清單

方法所屬類調用前提行為釋放鎖
wait()Object持有對象監視器鎖(在 synchronized 內)釋放鎖,進入等待集,阻塞直到:被 notify/notifyAll、超時、中斷、虛假喚醒是
notify()Object持有對象監視器鎖隨機喚醒一個在等待集的線程,移入入口集競爭鎖否(退出 synchronized 才釋放)
notifyAll()Object持有對象監視器鎖喚醒所有等待集線程,全部移入入口集否

總結

Object 監視器(wait/notify/notifyAll)是 Java 線程通信的基石:同步鎖為前提、等待集/入口集為模型、while 循環防虛假喚醒、notifyAll 保活性。掌握生產者消費者模式是並發編程入門必修課。現代項目推薦優先使用高級組件:BlockingQueue(ArrayBlockingQueue/LinkedBlockingQueue)、Lock + Condition、CompletableFuture、Disruptor,將同步協作細節封裝在庫中,業務代碼專注領域邏輯。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
坑點現象根因修正
IllegalMonitorStateException調用 wait/notify 拋異常未持有鎖、鎖對象不一致確保在 synchronized(同一對象) 內調用
虛假喚醒導致邏輯錯誤隊列明明空卻繼續 take用 if 代替 while統一用 while (condition) wait()
notify 喚醒同類導致死等生產者喚醒生產者,消費者永不醒單 notify 隨機性改用 notifyAll 或 Condition 精確喚醒
死鎖程序卡住無輸出鎖順序不一致、持有鎖調用外部方法固定鎖順序、縮小同步塊、用 tryLock 超時