天天看點

java線程之間的通信(等待/通知機制)

       線程開始運作,擁有自己的棧空間,就如同一個腳本一樣,按照代碼一步步的執行直到終止。但是,每個運作的線程,如果僅僅是孤立地運作,那麼沒有太大的價值,但如果多個線程能夠互相配合完成工作,這将會帶來巨大的價值。而java多線程的等待和通知機制就是用來完成線程之間的通信。

場景:

       一個線程修改了一個對象的值,而另一個線程感覺到了變化,然後進行響應的操作,整個過程開始于一個線程,而最終執行又是另一個線程。前者是生産者,後者是消費者,這種模式隔離了“做什麼”和“怎麼做”,在功能層面上實作了解耦,體系結構上具備了良好的伸縮性,但是在java語言中如何實作類似的功能呢?實作這種機制最簡單的方法就是讓消費者線程不斷的循環檢查變量是否符合預期,如下代碼所示:

while(value!=desire){ 
Thread.sleep(1000); 
} 
dosomething();            

上面的僞代碼在條件不滿足的時候就睡眠一段時間,這樣做的目的是防止過快的“無效”的嘗試,這種方法看似實作了所需的功能,但是卻存在如下問題:

1、難以確定及時性。在睡眠時,基本上不消耗處理器資源,但是如果睡得太久,就不能及時發現條件已經改變,也就是及時性難以保證。

2、難以降低開銷。如果降低了睡眠的時間,比如休眠1毫秒,這樣消費者能更加迅速地發現條件變化,但是卻可能消耗更多處理器資源,造成了無端的浪費。

        以上兩個問題看似難以解決,但是java通過内置的等待/通知機制能夠很好地解決這個沖突并實作所需的功能。 等待/通知的相關方法在任意java對象都具備,因為這些方法被定義在所有對象的超類java.lang.Object上。

等待/通知機制: 

      是指一個線程A調用了對象O的wait()方法進入等待狀态,而另一個線程B調用了對象O的notify()或者notifyAll()方法,線程A收到通知後從對象O的wait()方法傳回,進而執行後續操作。上述兩個線程通過對象O起來完成互動,而對象上的wait()和 notify() /notifyAll() 的關系就如同開關信号一樣,用來完成 等待方 與 通知方 的互動工作。

相關方法如下:

notify():通知一個在對象上等待的線程,使其從wait()方法傳回,而傳回的前提是該線程獲得到了對象的鎖。 

notifyAll():通知所有等待在該對象上的線程。 

wait():通知該方法線上程進入WAITING狀态,隻有等待另外線程的通知或者被中斷才會傳回,需要注意,調用wait()方法後,會釋放對象的鎖。 

wait(long):逾時等待一段時間,這裡的參數時間是毫秒,也就是等待長達n毫秒,如果沒有通知就逾時傳回。 

wait(long,int):對于逾時時間更加細粒度的控制,可以達到納秒。

細節: 

調用wait()、notify()、以及notifyAll()時需要注意的細節: 

1.使用wait()、notify()、notifyAll()時需要對調用對象加鎖。 

2.調用wait()方法後,線程狀态由RUNNING變為WAITING,并将目前線程放置到對象的等待隊列。 

3.notify()或者notifyAll()方法調用後,等待線程依舊不會從wait()傳回,需要調用notify()或者notifyAll()的線程釋放鎖之後,等待線程才有機會從wait()傳回。 

4.notify()方法将等待隊列中的一個線程從等待隊列中移到同步隊列中,而notifyAll方法則是将等待隊列中所有的線程全部移動到同步對列,被移動的線程狀态由WAITING變為BLOCKED。 

5.從wait()方法傳回的前提是獲得了調用對象的鎖。 

等待/通知機制依托于同步機制,其目的就是確定等待線程從wait()方法傳回能夠感覺線程對變量做出的修改。

繼續閱讀