在 Java 中,wait()
方法必須在 synchronized
方法或代碼塊中調用,主要原因如下:
1. 監視器鎖(Monitor)機制
- 依賴對象鎖:
wait()
方法需要操作對象的監視器鎖(Monitor),調用前必須持有該對象的鎖,否則會拋出IllegalMonitorStateException
。 - 釋放鎖:
wait()
會使當前線程釋放鎖并進入等待狀態,只有持有鎖的線程才能安全釋放鎖。
2. 避免競態條件
- 原子性保障:
wait()
和notify()
的調用需要與共享變量的修改保持原子性。若不加鎖,可能導致:- 競態條件:線程 A 檢查條件后、調用
wait()
前,線程 B 修改條件并調用notify()
,導致信號丟失(Missed Signal)。 - 虛假喚醒:線程可能因底層機制被意外喚醒,需通過循環檢查條件避免。
- 競態條件:線程 A 檢查條件后、調用
3. 線程安全與可見性
- 同步保證可見性:
synchronized
確保線程對共享變量的修改對其他線程立即可見,避免因緩存不一致導致的條件判斷錯誤。 - 防止并發沖突:若多個線程同時調用
wait()
或notify()
而無同步控制,會導致不可預測的行為。
4. 設計規范與異常處理
- JVM 強制約束:Java 規范明確要求
wait()
必須在同步上下文中調用,否則直接拋出異常。 - 鎖管理一致性:
wait()
和notify()
的設計初衷是配合synchronized
實現線程協作,確保鎖的獲取與釋放邏輯一致。
示例代碼
synchronized (lock) {while (!condition) { // 循環檢查條件,避免虛假喚醒lock.wait(); // 釋放鎖并等待}// 條件滿足后執行操作
}
關鍵點:
- 調用
wait()
前必須通過synchronized
獲取鎖。 - 使用循環檢查條件,而非
if
,確保喚醒后條件仍成立。
總結
原因 | 說明 |
---|---|
監視器鎖依賴 | wait() 需持有鎖才能操作對象監視器。 |
競態條件避免 | 同步塊保障條件檢查與 wait() 調用的原子性。 |
線程安全與可見性 | synchronized 確保共享狀態的一致性。 |
JVM 規范強制 | 非同步調用會拋出 IllegalMonitorStateException 。 |
通過 synchronized
的配合,wait()
能夠安全實現線程間的協作與通信。