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

Java 多執行緒核心:wait/notify 機制深度剖析與經典範例解析

     編輯:Java 程式設計入門

前言

wait()、notify() 是 Object 基礎方法,非 Thread 方法。每個物件天生擁有監視器鎖與等待集合,這兩個方法正是操作這套內建同步機制的鑰匙。

核心概念:鎖與等待集合

  • synchronized:獲取物件鎖,進入臨界區
  • wait():釋放鎖,將當前執行緒放入物件的等待集合,進入 WAITING 狀態
  • notify():從等待集合喚醒一個執行緒,移入 ENTRY SET 競爭鎖
  • notifyAll():喚醒所有等待執行緒 (推薦,避免訊號遺失)

wait vs sleep vs suspend

關鍵差異: wait() 釋放鎖,允許其他執行緒進入同步區;sleep() 佔著鎖睡覺,導致其他執行緒阻塞。

使用規則 (硬性約束)

  1. 必須在 synchronized 區塊/方法內 呼叫
  2. 鎖物件 = 呼叫 wait/notify 的物件 = synchronized 括號內的物件
  3. 若違反 → IllegalMonitorStateException

經典範例:ThreadA 等待 ThreadB 計算完成

class ThreadB extends Thread {
    int total = 0;
    @Override
    public void run() {
        synchronized (this) {          // 鎖定 this (ThreadB 實例)
            for (int i = 0; i < 100; i++) total += i;
            notify();                  // 喚醒等待 this 鎖的執行緒
        }                              // 釋放鎖
    }
}

public class ThreadA {
    public static void main(String[] args) {
        ThreadB b = new ThreadB();
        b.start();                     // 啟動計算執行緒
        System.out.println("b started");
        
        synchronized (b) {             // 主執行緒鎖定同一個 b 物件
            try {
                System.out.println("Waiting for b...");
                b.wait();              // 釋放 b 的鎖,進入等待集合
                System.out.println("Completed. Back to main.");
            } catch (InterruptedException e) {}
        }                              // 重新獲得鎖後離開同步區
        System.out.println("Total: " + b.total);
    }
}

執行流程時序圖

ThreadA (main)                    ThreadB
    |                                 |
    |-- b.start() ------------------->|
    |                                 | synchronized(this) 獲得鎖
    |                                 | for 迴圈計算 total
    |                                 | notify() 喚醒等待者
    |                                 | } 釋放鎖
    | synchronized(b) 獲得鎖          |
    | b.wait() 釋放鎖、進入等待集合   |
    |        (阻塞)                   |
    |            <--- 喚醒 (notify)   |
    | 重新競爭鎖、獲得鎖              |
    | 繼續執行 wait() 後程式碼        |
    | } 釋放鎖                        |
    | 印出 total                      |

常見錯誤與修正

方法釋放鎖喚醒方式用途
wait()是notify/notifyAll / timeout協調多執行緒存取共享資源
sleep()否 (持有鎖睡眠)時間到期單純延遲
suspend()否 (已棄用)resume() (已棄用)不建議使用
錯誤代碼問題修正
synchronized(obj) { obj.wait(); } 但 notify() 在另一物件上鎖物件不一致,喚醒失效確保 wait/notify 作用於同一物件實例
未在 synchronized 內呼叫 wait()IllegalMonitorStateException包裹在 synchronized(同一物件) 內
使用 notify() 導致訊號遺失多執行緒等待時,單一 notify 可能喚醒錯誤執行緒優先使用 notifyAll(),配合條件迴圈檢查
wait() 不在迴圈中檢查條件虛假喚醒 導致邏輯錯誤while (!condition) wait();

正確範本:條件等待模式

// 等待方
synchronized (lock) {
    while (!condition) { // 必須用 while,防虛假喚醒
        lock.wait();
    }
    // 條件滿足,執行後續
}

// 通知方
synchronized (lock) {
    condition = true; // 更新共享條件
    lock.notifyAll(); // 喚醒所有等待者
}

現代替代:java.util.concurrent

  • Condition:lock.newCondition() → await()/signal()/signalAll(),支援多條件變數
  • CountDownLatch:一次性閂鎖,等待 N 次事件
  • CyclicBarrier:可重用柵欄,執行緒互等
  • Phaser:階段式同步
  • CompletableFuture:非同步組合

總結

wait/notify 是 Java 同步機制的原子操作。掌握「鎖釋放→等待→喚醒→重獲鎖」流程,配合 while 條件檢查與 notifyAll,可構建正確的執行緒協作。實務開發優先使用 JUC 高階工具,降低認知負擔與 Bug 風險。

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