Object.wait()/notify() 是 Java 內置的線程協作機制,基於對象監視器鎖實現條件等待與通知。本文通過一個經典案例——兩線程交替打印 1-100,每線程每次打印 10 個數字,詳細講解鎖關聯、wait/notify 流程、代碼實現細節及常見錯誤避坑指南。
synchronized(obj) 獲取鎖,進入臨界區。obj.wait():**釋放鎖**,當前線程進入對象的等待隊列阻塞。obj.notify():**喚醒等待隊列中任一線程**(隨機),該線程從 wait() 返回前需重新競爭獲取鎖。notifyAll() 喚醒隊列中**所有**線程,推薦使用以防信號丟失。DemoThread 實例(實現 Runnable),持有計數器 time。TestThread 實例,名稱 "1" 和 "2",共享同一 DemoThread 任務對象。synchronized(this) 調用 wait() 釋放鎖等待。notify() 喚醒線程 "2",自身 wait() 等待。notify() 喚醒線程 "1",自身 wait()。
package jdeveloper.study;
public class DemoThread implements Runnable {
public DemoThread() {
TestThread t1 = new TestThread(this, "1");
TestThread t2 = new TestThread(this, "2");
t2.start(); // 關鍵:先啟動線程2,使其先進入 wait()
t1.start();
}
public static void main(String[] args) {
new DemoThread();
}
@Override
public void run() {
TestThread t = (TestThread) Thread.currentThread();
try {
// 線程2先進入等待,線程1直接繼續
if (!t.getName().equalsIgnoreCase("1")) {
synchronized (this) {
wait();
}
}
while (true) {
System.out.println("@time in thread " + t.getName() + " = " + t.increaseTime());
// 每10個數字交換控制權
if (t.getTime() % 10 == 0) {
synchronized (this) {
System.out.println("****************************************");
notify(); // 喚醒另一線程
if (t.getTime() == 100) break; // 結束條件
wait(); // 釋放鎖並等待
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class TestThread extends Thread {
private int time = 0;
public TestThread(Runnable r, String name) { super(r, name); }
public int getTime() { return time; }
public int increaseTime() { return ++time; }
}
t2.start() 先於 t1.start(),線程 "2" 率先進入 run(),因名稱非 "1" 進入 synchronized(this){ wait(); } 釋放鎖並阻塞。線程 "1" 隨後啟動,獲取鎖直接進入打印循環。若順序反轉,線程 "1" 會先進入 wait() 導致死等。
同步塊僅包裹 notify()/wait() 及結束判斷,打印與計數在同步塊外執行,最大化併發窗口(雖本例為交替執行,併發度為 1,但原則通用)。
TestThread 封裝 time 計數器,每線程實例獨立計數。但因共享同一 DemoThread 任務對象,run() 中通過 Thread.currentThread() 區分線程身份,配合各自計數器實現全局 1-100 有序推進。
if (t.getTime() == 100) break; 放在 notify() 之後、wait() 之前,確保最後一輪打印完成後主動退出,不再進入等待,避免另一線程永久阻塞。
| 錯誤模式 | 後果 | 修正 |
|---|---|---|
用 if 代替 while 檢查條件 | 虛假喚醒導致邏輯錯誤 | 永遠用 while(條件不滿足) wait() |
| 不同線程同步於不同鎖對象 | wait/notify 失效,線程不互通 | 共享同一任務實例,同步於 this 或共享 final Object lock |
| notify() 後未釋放鎖就執行耗時操作 | 被喚醒線程獲取不到鎖,併發度降為串行 | notify() 後盡快退出同步塊 |
| 缺少啟動順序控制 | 雙方同時 wait() 導致死鎖 | 明確協作協議:一方先 wait,另一方先 notify |
實際項目中,推薦使用更高級、語義更清晰的工具:
ReentrantLock + Condition:支持多條件隊列、可中斷等待、公平鎖。Phaser / CyclicBarrier:多方同步屏障,適合階段性協作。Semaphore:信號量控制資源訪問許可。BlockingQueue:生產消費模式首選,內置阻塞協作。
javac -d . DemoThread.java
java -cp . jdeveloper.study.DemoThread
掌握 wait/notify 核心協作模式,是理解 Java 併發本質、排查線程協作 Bug、設計高級同步組件的基石。