核心差異速查表
| 維度 | Thread.sleep() | Object.wait() |
| 所屬類 | Thread (靜態方法) | Object (實例方法) |
| 鎖行為 | 不釋放 當前持有的對象鎖 | 釋放 當前對象鎖,進入等待集 |
| 喚醒方式 | 時間到自動喚醒 | 需 notify()/notifyAll() 喚醒 (或中斷/超時) |
| 調用前提 | 無 | 必須持有該對象鎖 (在 synchronized 內) |
| 拋出異常 | InterruptedException | InterruptedException |
| 典型用途 | 定時延遲、輪詢間隔 | 線程協作、生產者消費者 |
代碼演示:wait/notify 協作 vs sleep 獨立運行
public class WaitVsSleep {
static final Object LOCK = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (LOCK) {
System.out.println("T1: 進入同步塊,調用 wait() 釋放鎖");
try { LOCK.wait(); } catch (InterruptedException ignored) {}
System.out.println("T1: 被喚醒,繼續持有鎖執行");
}
}, "T1").start();
try { Thread.sleep(1000); } catch (InterruptedException ignored) {}
new Thread(() -> {
synchronized (LOCK) {
System.out.println("T2: 獲得鎖,調用 notify() 喚醒 T1");
LOCK.notify();
System.out.println("T2: 睡眠 2s (持有鎖),T1 必須等待");
try { Thread.sleep(2000); } catch (InterruptedException ignored) {}
System.out.println("T2: 釋放鎖");
}
}, "T2").start();
}
}
/* 輸出順序:
T1: 進入同步塊,調用 wait() 釋放鎖
T2: 獲得鎖,調用 notify() 喚醒 T1
T2: 睡眠 2s (持有鎖),T1 必須等待
T2: 釋放鎖
T1: 被喚醒,繼續持有鎖執行
*/
關鍵觀察
wait() 釋放鎖 → T2 能進入同步塊 → notify() 將 T1 從等待集移至入口集 → T1 仍需等待 T2 釋放鎖 (退出 synchronized) 才能繼續。
sleep() 不釋放鎖 → 若 T2 在 synchronized 內 sleep,T1 無法進入。
- 若註釋
LOCK.notify(),T1 永久等待 (程序掛起)。
進階:wait(long timeout) 超時機制
synchronized (obj) {
obj.wait(5000); // 5秒後自動喚醒 (若未被 notify)
// 醒來後需重新檢查條件 (spurious wakeup 防禦)
}
最佳實踐
- 同步塊內使用
wait/notify 實現線程協作 (生產者消費者、線程池任務隊列)。
- 單純延遲、輪詢間隔用
Thread.sleep 或 TimeUnit.MILLISECONDS.sleep。
- 永遠在循環中調用 wait():
while (conditionNotMet) obj.wait(); 防止虛假喚醒。
- 優先使用
java.util.concurrent 高級組件 (BlockingQueue, CountDownLatch, Semaphore, Condition) 替代底層 wait/notify。