多線程notifyAll 和 notify

當我們做多線程同步時,經常會用到等待通知機制,而 Java 中,它的實現方式相信大家也很熟悉, 就是 synchronized 配合 wait()、notify()、notifyAll() 這三個方法來實現。

最常見的例子就是生產者消費者模式,如果你的Java程序中有兩個線程-生產者和消費者,生產者線程在緩衝區爲滿的時候,消費者在緩衝區爲空的時候,或者是線程條件不滿足時,這時線程都應該開始等待,當然循環等待也行但是太消耗資源,最好的方法就是線程此時都應該暫停運行(wait()),等到線程不滿,以及滿足一定的條件時再重新喚醒線程。

喚醒線程有兩種方式notify() 和 notifyAll() ,兩種有何不同也簡單字面意思就是喚醒一個等待線程,還是喚醒所有等待線程。兩種方式各有各的好處但是本人推薦notifyAll()。因爲當只有一個等待線程時兩種方法沒什麼區別,但是當有多個等待線程時,notify()只會通知一個很容易造成死鎖。理論上一個wait一個notify是很完美,但是是理想環境時,大部分都不行。

舉個例子,在多生產者消費者模式下,待處理的數據隊列只有一條數據了,理想場景下,消費者在處理掉一條數據後,理論上應該喚醒生產者再生產一條新的待消費數據。可是 notify() 是隨機喚醒,也就是它可能會喚醒一個消費者線程,這個消費者線程,發現沒有待處理的數據,此時條件不滿足,又主動進入等待隊列。

也正是因爲如此,在併發編程中有個範式模板:

  synchronized(this){
    while(不滿足條件) {
      wait();
    }
  }

notify() 只能確保喚醒一個線程,但是不保證線程執行的時候,曾經的等待條件已經被滿足了。爲了保證可靠性,所以此處使用循環檢測的方式,只有必要條件滿足時,才繼續執行。正是因爲 notify() 隨機喚醒的特點,導致在多條件的情況下,會導致某些線程永遠不會被通知到。穩妥的方式,是使用 notifyAll(),讓等待中的線程,都有一次再執行的權利。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章