在 Java 多線程開發中,wait()、notify()、sleep()、interrupt() 是線程協作與控制的基石。混淆它們的行為差異是導致死鎖、活鎖、線程饑餓等併發 Bug 的常見原因。本文從鎖行為、喚醒機制、適用場景等維度進行系統性對比,助你構建清晰的線程控制心智模型。
sleep(long millis):暫停當前線程,指定時間後自動恢復運行狀態,不釋放鎖。yield():暫停當前線程,主動讓出 CPU 時間片,進入就緒狀態,不釋放鎖,可能立即再次獲取調度。join():等待目標線程終止,底層調用 wait(0) 實現。interrupt():設置中斷標誌位,不直接停止線程。若目標線程處於阻塞狀態(wait/sleep/join/I/O),將拋出 InterruptedException 或 ClosedByInterruptException 並清除標誌位。wait():釋放當前對象鎖,進入等待集合,直到 notify/notifyAll、超時或中斷被喚醒,重新競爭鎖後繼續執行。notify():隨機喚醒一個在該對象上等待的線程。notifyAll():喚醒所有在該對象上等待的線程。| 維度 | sleep() | wait() |
|---|---|---|
| 所屬類 | Thread(靜態) | Object(實例) |
| 鎖行為 | 不釋放 同步鎖 | 完全釋放 對象鎖 |
| 喚醒方式 | 時間到期 / interrupt() |
時間到期 / notify/notifyAll / interrupt() |
| 調用前提 | 無特殊要求 | 必須在 synchronized 同步塊/方法中,持有對象監視器 |
| 典型場景 | 線程內部節奏控制(延遲、輪詢間隔) | 線程間通信(生產者-消費者、狀態變更通知) |
| 異常處理 | 拋出 InterruptedException,需捕獲/拋出 |
拋出 InterruptedException,需捕獲/拋出 |
// sleep 簽名
public static void sleep(long millis) throws InterruptedException
public static void sleep(long millis, int nanos) throws InterruptedException
// wait 簽名
public final void wait() throws InterruptedException
public final void wait(long timeout) throws InterruptedException
public final void wait(long timeout, int nanos) throws InterruptedException
wait/notify/notifyAll 的線程必須是該對象監視器的擁有者(即在 synchronized(obj) 內部),否則拋出 IllegalMonitorStateException。synchronized (lock) {
while (!condition) { // 而非 if
lock.wait();
}
// 執行業務邏輯
}
interrupt() 後,線程會先嘗試重新獲取對象鎖,獲得鎖後才拋出 InterruptedException。核心原理:interrupt() 僅修改線程的中斷標誌位(boolean 型),不會強制終止線程。真正的中斷響應依賴於線程自身代碼的配合:
InterruptedException 並清除標誌位。interrupt() 僅設置標誌位,線程不會自動停止,需通過 Thread.interrupted() 或 isInterrupted() 主動輪詢並處理。最佳實踐:捕獲
InterruptedException後,應重新設置中斷標誌(Thread.currentThread().interrupt())或向上拋出,保持中斷語義傳遞,避免吞噬中斷導致調用方無法感知。
LockSupport.park())替代。sleep | ❌ | 時間到 / 中斷 | 線程級 | 定時延遲、節奏控制 |
| wait | ✅ | 通知 / 時間到 / 中斷 | 對象級 | 線程間協作、條件等待 |
| notify | ❌ | — | 對象級 | 單線程喚醒(需謹慎防漏喚醒) |
| notifyAll | ❌ | — | 對象級 | 廣播喚醒(推薦默認選擇) |
| interrupt | ❌ | 設置標誌位 | 線程級 | 取消任務、打破阻塞 |
| yield | ❌ | 調度器決定 | 線程級 | 禮讓 CPU(極少用) |
java.util.concurrent 包下的高級組件(CountDownLatch、CyclicBarrier、Condition、BlockingQueue),減少直接操作底層 wait/notify。notifyAll 替代 notify 避免信號丟失。sleep 或耗時操作,降低鎖競爭粒度。