Object.wait()、notify()、notifyAll() 是 Java 物件監視器提供的基礎執行緒通訊原語。本文透過一個「司機-乘客-汽車」模擬範例,深入解析其正確使用模式、常見錯誤與現代替代方案。
// 共享資源:汽車
class Car {
synchronized public void sleep() { // 乘客上車 → 司機等待
try { wait(); } catch (Exception e) {}
}
synchronized public void wake() { // 乘客下車 → 通知司機
notify();
}
}
// 乘客執行緒:週期性上車、下車
class Passenger implements Runnable {
private Car car;
public Passenger(Car c) { this.car = c; }
public void run() {
while (true) {
Thread.yield();
try { Thread.sleep(3000); } catch (InterruptedException ie) {}
System.out.println("a passenger go on");
car.wake(); // 通知司機有人上車
System.out.println("waiting stop and sleep...");
car.sleep(); // 乘客等待下車
}
}
}
// 司機執行緒:等待乘客、開車
class Driver implements Runnable {
private Car car;
public Driver(Car c) { this.car = c; }
public void run() {
while (true) {
System.out.println("wait a passenger.......");
car.sleep(); // 司機等待乘客
try { Thread.sleep(3000); } catch (InterruptedException ie) {}
Thread.yield();
try { Thread.sleep(100); } catch (InterruptedException ie) {}
car.wake(); // 通知乘客到站
System.out.println(" passenger wake and go ");
}
}
}
// 主程式啟動
public class Road {
public static void main(String[] args) {
Car c = new Car();
new Thread(new Driver(c)).start();
new Thread(new Passenger(c)).start();
}
}
wait()/notify() 必須 在同步方法或同步區塊(synchronized(this))中呼叫IllegalMonitorStateExceptionsynchronized,滿足條件notify()/notifyAll()、interrupt()、或超時(wait(timeout))wait() 返回繼續執行notify():隨機喚醒一個等待執行緒notifyAll():喚醒所有等待執行緒car.sleep() → wait() 釋放鎖、等待car.wake() → notify() 喚醒司機car.wake() 喚醒乘客car.sleep() 等待下一趟| 錯誤/風險 | 說明 | 修正 |
|---|---|---|
| 未在同步區塊呼叫 wait/notify | 拋出 IllegalMonitorStateException | 確保在 synchronized 方法/區塊中 |
| 使用 if 而非 while 檢查條件 | 虛假喚醒導致邏輯錯誤 | 標準模式:while (!condition) wait(); |
| notify() 導致信號遺失 | 多執行緒等待同一條件時,隨機喚醒可能非目標執行緒 | 改用 notifyAll() 或 java.util.concurrent 機制 |
| 忽略 InterruptedException | 吞掉中斷信號,導致執行緒無法優雅關閉 | 重新設置中斷旗標或拋出 |
| 鎖持有時間過長 | 在同步區塊執行耗時操作(sleep、I/O),降低並發度 | 縮小同步範圍,僅保護共享狀態讀寫 |
Java 5+ java.util.concurrent 提供更高階、更安全的協作工具:
ArrayBlockingQueue、LinkedBlockingQueue —— 內建阻塞 put/take,完美替代生產者-消費者Lock.newCondition() 支援多條件變數、可中斷等待、公平鎖BlockingQueue<Passenger> queue = new LinkedBlockingQueue<>();
// 乘客執行緒
new Thread(() -> {
while (true) {
Thread.sleep(3000);
queue.put(new Passenger()); // 滿則阻塞
System.out.println("passenger go on");
}
}).start();
// 司機執行緒
new Thread(() -> {
while (true) {
Passenger p = queue.take(); // 空則阻塞
System.out.println("driver take passenger");
Thread.sleep(3000); // 開車
System.out.println("arrived");
}
}).start();
wait()/notify() 是理解 JVM 監視器機制的基礎,但實務開發極少直接使用。掌握其原理有助於除錯與理解底層庫,生產代碼請優先選用 java.util.concurrent 高階組件,獲得更好的安全性、可讀性與效能。