生產者消費者模式中條件判斷是使用while而不是if

永遠在循環(loop)裏調用 wait 和 notify,不是在 If 語句現在你知道wait應該永遠在被synchronized的背景下和那個被多線程共享的對象上調用,下一個一定要記住的問題就是,你應該永遠在while循環,而不是if語句中調用wait。因爲線程是在某些條件下等待的——在我們的例子裏,即“如果緩衝區隊列是滿的話,那麼生產者線程應該等待”,你可能直覺就會寫一個if語句。但if語句存在一些微妙的小問題,導致即使條件沒被滿足,你的線程你也有可能被錯誤地喚醒。所以如果你不在線程被喚醒後再次使用while循環檢查喚醒條件是否被滿足,你的程序就有可能會出錯——例如在緩衝區爲滿的時候生產者繼續生產數據,或者緩衝區爲空的時候消費者開始消耗數據。所以記住,永遠在while循環而不是if語句中使用wait!

因爲在多核處理器環境中,Signal喚醒操作可能會激活多於一個線程(阻塞在條件變量上的線程),使得多個調用等待的線程返回。所以用while循環對condition多次判斷,可以避免這種假喚醒。

基於以上認知,下面這個是使用wait和notify函數的規範代碼模板:

// The standard idiom for calling the wait method in Java
synchronized(sharedObject) {
    while(condition) {
        sharedObject.wait();
        // (Releases lock, and reacquires on wakeup)
}
// do action based upon condition e.g. take or put into queue
}

Ps:在while循環裏使用wait的目的,是在線程被喚醒的前後都持續檢查條件是否被滿足。如果條件並未改變,wait被調用之前notify的喚醒通知就來了,那麼這個線程並不能保證被喚醒,有可能會導致死鎖問題。

注意:

  1. 永遠在synchronized的方法或對象裏使用wait、notify和notifyAll,不然Java虛擬機會生成 IllegalMonitorStateException。
  2. 永遠在while循環裏而不是if語句下使用wait。這樣,循環會在線程睡眠前後都檢查wait的條件,並在條件實際上並未改變的情況下處理喚醒通知。
  3. 永遠在多線程間共享的對象(在生產者消費者模型裏即緩衝區隊列)上使用wait。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章