| 方法 | 釋放鎖 | 喚醒方式 | 用途 |
wait() | 是 | notify/notifyAll / timeout | 協調多執行緒存取共享資源 |
sleep() | 否 (持有鎖睡眠) | 時間到期 | 單純延遲 |
suspend() | 否 (已棄用) | resume() (已棄用) | 不建議使用 |
關鍵差異: wait() 釋放鎖,允許其他執行緒進入同步區;sleep() 佔著鎖睡覺,導致其他執行緒阻塞。
使用規則 (硬性約束)
- 必須在 synchronized 區塊/方法內 呼叫
- 鎖物件 = 呼叫
wait/notify 的物件 = synchronized 括號內的物件
- 若違反 →
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 |
常見錯誤與修正
| 錯誤代碼 | 問題 | 修正 |
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 風險。
- 上一篇文章: 利用 JavaMail 收取 Hotmail 退信:multipart/report 處理實作
- 下一篇文章: Robocode 應用效能優化:線程調度與回合制機制分析
Copyright ©
程式碼之家 All Rights Reserved