wait()、notify()、notifyAll() 是 Object 類定義的基礎協作方法,構成了 Java 早期線程通信的基石。本文系統梳理其使用規則、執行流程、中斷處理、通知順序陷阱及條件判斷最佳實踐。
wait/notify 操作的是對象監視器(Monitor),調用前必須持有該對象的鎖,否則拋出 IllegalMonitorStateException。標準模式:
synchronized (lockObj) {
// 檢查條件
while (conditionNotMet) {
lockObj.wait(); // 釋放鎖,進入等待隊列
}
// 執行業務邏輯
lockObj.notifyAll(); // 喚醒等待線程
}
synchronized 塊,獲取對象鎖。wait():釋放鎖,線程進入該對象的 等待集合,釋放 CPU。notify/notifyAll():從等待集合移出一個/所有線程到 入口集合 競爭鎖。notify 後不立即釋放鎖,需退出 synchronized 塊才釋放。喚醒的線程必須重新獲取鎖才能從 wait() 返回繼續執行。// 錯誤示範:notify 後執行耗時操作導致喚醒線程無法及時獲取鎖
public void synNotifyMethod(Object lock) {
synchronized (lock) {
lock.notify();
Thread.sleep(5000); // 持有鎖睡眠,喚醒線程被阻塞
} // 此處才釋放鎖
}
調用 thread.interrupt() 可打斷 wait(),拋出 InterruptedException。注意區分:
Thread.interrupted():靜態方法,檢測當前線程中斷狀態並清除標誌。thread.isInterrupted():實例方法,檢測指定線程中斷狀態,不清除標誌。若 notify() 先於 wait() 執行,等待線程將永久阻塞(除非被中斷)。解決:確保先 wait 後 notify,或使用 CountDownLatch/Semaphore 替代。
while 而非 if?經典場景:兩個消費者線程,一個生產者只生產一個元素。
if 判斷:第一個消費者消費後,第二個被喚醒直接執行刪除 → IndexOutOfBoundsException。
while 循環:被喚醒後重新檢查條件,發現隊列為空 → 繼續 wait(),避免越界。
// 正確模式
synchronized (lock) {
while (list.isEmpty()) { // 循環檢查,防止虛假喚醒/條件變化
lock.wait();
}
list.remove(0); // 安全執行
}
線程可能在無 notify、無中斷的情況下自發醒來(JVM 允許),while 循環可有效防禦。
Lock + Condition 替代JDK 1.5+ 推薦使用 ReentrantLock 配合 Condition,支持多條件隊列、可中斷等待、公平鎖等高級特性,語義更清晰。
wait/notify 在 synchronized(lockObj) 內調用while 循環notifyAll() 優於 notify()(避免信號丟失)notify 後盡快退出同步塊InterruptedException 後保留中斷狀態或拋出BlockingQueue/Lock-Condition/CompletableFuture