天天看點

java thread中的wait()和notify()

java thread有五種狀态類型

建立狀态(new):新建立了一個線程對象。

就緒狀态(runnable):線程對象建立後,其他線程調用了該對象的start()方法。該狀态的線程位于可運作線程池中,變得可運作,等待擷取cpu的使用權。

運作狀态(running):就緒狀态的線程擷取了cpu,執行程式代碼。

阻塞狀态(blocked):塞狀态是線程因為某種原因放棄cpu使用權,暫時停止運作。直到線程進入就緒狀态,才有機會轉到運作狀态。

死亡狀态(dead):線程執行完了或者因異常退出了run()方法,該線程結束生命周期。

當我們調用線程類的<code>sleep()、suspend()、yield()、wait()</code>等方法時會導緻線程進入阻塞狀态。

wait(): 調用任何對象的wait()方法會讓目前線程進入等待,直到另一個線程調用同一個對象的notify()或notifyall()方法。

notify():喚醒因調用這個對象wait()方法而阻塞的線程。

首先,<code>sleep()、suspend()、yield ()</code>等方法都隸屬于 thread 類,但<code>wait()/notify()</code>這一對卻直接隸屬于object 類,也就是說,所有對象都擁有這一對方法。初看起來這十分不可思議,但是實際上卻是很自然的,因為這一對方法阻塞時要釋放占用的鎖,而鎖是任何對象都具有的,調用對象的 wait() 方法導緻線程阻塞,并且該對象上的鎖被釋放。而調用對象的notify()方法則導緻因調用該對象的 wait() 方法而阻塞的線程中随機選擇的一個解除阻塞(但要等到獲得鎖後才真正可執行)。

其次,前面叙述的所有方法都可在任何位置調用,但是這一對方法卻必須在 <code>synchronized</code>方法或塊中調用,理由也很簡單,隻有在<code>synchronized</code> 方法或塊中目前線程才占有鎖,才有鎖可以釋放。同樣的道理,調用這一對方法的對象上的鎖必須為目前線程所擁有,這樣才有鎖可以釋放。是以,這一對方法調用必須放置在這樣的 <code>synchronized</code>方法或塊中,該方法或塊的上鎖對象就是調用這一對方法的對象。若不滿足這一條件,則程式雖然仍能編譯,但在運作時會出現<code>illegalmonitorstateexception</code>異常。

最後,關于 wait() 和 notify() 方法再說明三點:

調用 notify() 方法導緻解除阻塞的線程是從因調用該對象的 wait() 方法而阻塞的線程中随機選取的,我們無法預料哪一個線程将會被選擇,是以程式設計時要特别小心,避免因這種不确定性而産生問題

除了 notify(),還有一個方法 notifyall() 也可起到類似作用,唯一的差別在于,調用 notifyall() 方法将把因調用該對象的 wait() 方法而阻塞的所有線程一次性全部解除阻塞。當然,隻有獲得鎖的那一個線程才能進入可執行狀态。

wait()和notify()必須成對存在。

先寫一個計數線程類countthread:

再寫一個測試類作為主線程:

運作結果:

生産者:

消費者:

列印結果: