【Java併發】等待/通知機制詳解

轉載請註明出處https://blog.csdn.net/fury97/article/details/81336047

目錄

等待/通知機制

同步隊列

示例

舉例說明

應用場景

等待/通知的經典範式

Thread.join() 



等待/通知機制

當一個線程修改了一個對象的值,而另一個線程感知到了變化,然後進行相應的操作,整個過程開始於一個線程,最終執行的又是另一個線程。 
前者是生產者,後者是消費者。

等待/通知機制,是指一個線程A調用了對象O的wait()方法進入等待狀態,而另一個線程B調用了對象O的notify()方法或者notifyAll()方法,線程A收到通知後從對象O的wait()方法返回,繼續執行後續的操作。上述兩個線程通過對象O來進行交互,對象O的wait()和notify()就像開關信號一樣。


在繼續介紹之前,我們先了解一下使用synchronized關鍵字時,對象,監視器,同步隊列和執行線程之間的關係。

 

同步隊列

synchronized關鍵字對對象的獲取鎖與釋放鎖,實質上是使用了monitorenter和monitorexit指令,本質是對於一個對象的監視器(monitor)的獲取與釋放,這個獲取是排他的,也就意味着同一時刻只能有一個線程獲取到對象的監視器。

由圖我們可以看到這樣的過程:

  1. 線程訪問對象,首先使用monitor.enter來獲取對象監視器。
  2. 如果獲取成功,則線程繼續執行。
  3. 如果獲取失敗,說明已有其他線程獲得了該對象的鎖,則該線程進入同步隊列,線程狀態變爲BLOCKED(阻塞)。
  4. 當之前已獲得該對象的鎖的線程釋放鎖時,該釋放操作會喚醒阻塞在同步隊列中的線程,使其繼續嘗試獲取該對象的監視器。

瞭解了同步隊列之後,我們進入正題,看一個等待/通知的示例。

 

示例

由圖我們可以看到這樣的過程:

  1. WaitThread首先Monitor.Enter獲取對象的監視器(獲取鎖),獲取成功後,調用對象的wait()方法,進入了等待隊列WaitQueue,進入等待狀態,同時釋放了鎖(這個很關鍵,調用wait的同時會釋放鎖)。
  2. NotifyThread之前因爲獲取鎖失敗,進入了同步隊列阻塞中,由於WaitThread釋放了鎖,NotifyThread自然獲得了對象的鎖,並繼續執行,調用了對象的notify()方法。
  3. WaitQueue等待隊列中的WaitThread收到了notify信號,從WaitQueue等待隊列遷移至同步隊列,WaitThread從等待狀態進入阻塞狀態。
  4. NotifyThread執行完畢,釋放了鎖,由於WaitQueue在同步隊列中,自然就獲得了鎖,然後繼續執行。

 

舉例說明

我們舉一個淺顯易懂的例子: 
1. 有一家餐廳專門做外賣,這家餐廳有一個取餐窗口(同步隊列)和一個取餐等待區(等待隊列),取餐窗口只有一個服務員(對象的鎖),同時只能接待一個外賣小哥(線程)。 
2. 外賣小哥一到餐廳,如果沒有人排隊,就直接去窗口辦事,如果有人,就排隊等待服務員接待。 
3. 服務員接待到外賣小哥時,外賣小哥首先詢問他的餐是否已做好,如果做好,則直接拿走,如果沒有做好,則被安排到等待區休息(wait)。 
4. 等待區有很多的外賣小哥在等餐,餐好了會被叫號(notify),直接去窗口繼續排隊領餐。 
5. 每個外賣小哥領完餐,就會離開窗口(釋放鎖),下一個外賣小哥進入窗口領餐(獲得鎖)。


不知道大家看了這個例子以後,能搞清楚了嗎~

 

應用場景

當生產速度跟不上消費速度的時候,可以很大程度的提升效率。 
例如上述例子中,如果外賣小哥A的飯沒做好,那麼他一直在窗口等的話,後面的全部都要排隊等待,即使後面的小哥B餐好了,也要等待小哥A拿到餐以後纔可以進入窗口領餐。如果使用等待/通知機制來處理的話,小哥A的沒好,那麼就先讓他去旁邊等,先處理後面小哥B、C的事情,當小哥A的餐好的時候再叫他來拿,極大程度的提升了效率,減少了平均等待時間

由此,我們可以總結出等待/通知的經典範式。

 

等待/通知的經典範式

等待方(消費者):

  1. 獲取對象的鎖。
  2. 如果條件不滿足,那麼調用對象的wait()方法,被通知後仍要檢查條件是否滿足。
  3. 條件滿足則執行對應的邏輯。

通知方(生產者): 
1. 獲取對象的鎖。 
2. 改變條件。 
3. 通知等待在對象上的線程notify() /通知所有等待在對象上的線程。

 


Thread.join() 

Java中的Thread.join()方法的源碼中,也是採用的等待/通知機制,當線程A中調用線程B.join(),線程A會等待線程B執行完的通知後再繼續執行。

 

個人學習總結,有不對的地方歡迎指正。

參考資料:《Java併發編程的藝術》。

 

 

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