在 Java 中,wait()
方法通常與 synchronized
關鍵字一起使用,以確保執行緒之間的正確同步。wait()
方法使當前執行緒進入等待狀態,直到其他執行緒呼叫 notify()
或 notifyAll()
方法來喚醒它。
在 Java 併發程式設計中,wait()
方法通常放在 while
迴圈中而不是 if
語句中,這種做法主要是爲了解決下面的問題:
防止虛假喚醒
確保執行緒被喚醒時滿足條件,避免因條件變化導致執行緒繼續執行錯誤的操作
什麼是虛假喚醒(spurious wakeups)
虛假喚醒是指執行緒在沒有被顯式喚醒(例如透過 notify()
或 notifyAll()
)的情況下,仍然從 wait()
狀態中恢復執行的現象。
這種情況可能因為作業系統的執行緒排程或其他執行緒的干擾而發生。如果 wait()
方法放在 if
語句中,一旦執行緒被虛假喚醒,它可能會在沒有獲得鎖的情況下退出迴圈,從而導致程式邏輯錯誤。
使用 while 迴圈的原因
防止虛假喚醒
Java 中的 wait()
方法可能會出現“虛假喚醒”現象,即執行緒在沒有被其他執行緒呼叫 notify()
或 notifyAll()
的情況下被喚醒,如果不在迴圈中檢查等待條件,程式就會在沒有滿足結束條件的情況下退出。
爲了避免這種情況,可以將 wait()
放在 while
迴圈中,每次從 wait()
方法返回後,都需要重新檢查條件是否滿足,這樣可以確保執行緒在等待條件未滿足前,會持續等待。
即使執行緒因為虛假喚醒而提前醒來,迴圈會繼續檢查條件,如果條件不滿足,執行緒會再次呼叫 wait()
方法,從而避免虛假喚醒對程式邏輯的影響。
使用 while
迴圈可以確保在每次從 wait()
方法返回後,都會重新檢查條件是否滿足,從而避免因虛假喚醒而導致的錯誤。以下是一個典型的使用場景:
synchronized (monitor) { while (!condition) { try { monitor.wait(); } catch (InterruptedException e) { // 處理中斷異常 } } // 條件滿足,繼續執行 }
在這個例子中,monitor
是一個物件,condition
是一個布林條件。
執行緒在進入 synchronized
塊後,會檢查 condition
是否為 true
。如果條件不滿足,執行緒會呼叫 monitor.wait()
進入等待狀態,並釋放鎖。
當其他執行緒呼叫 monitor.notify()
或 monitor.notifyAll()
時,等待的執行緒會被喚醒,並重新進入 synchronized
塊。此時,執行緒會再次檢查 condition
,只有在條件滿足的情況下才會繼續執行。
確保條件滿足
當執行緒被喚醒時,它需要重新檢查等待的條件是否仍然滿足。如果條件不滿足,執行緒應該再次進入等待狀態。
透過在 while
迴圈中呼叫 wait()
,可以確保執行緒在每次喚醒後都重新檢查條件,從而避免因條件變化而導致的錯誤行為。
避免條件變化
在多執行緒環境中,其他執行緒可能會在當前執行緒進入等待狀態後修改條件。如果 wait()
方法放在 if
語句中,當執行緒被喚醒時,可能無法及時檢測到條件的變化,從而導致執行緒繼續執行錯誤的操作。