educoder 使用线程锁(lock)实现线程同步_线程间的通信(一)
這篇文章主要從4個角度來講多線程間的通信:
等待/通知機(jī)制的實現(xiàn):
(1)wait()方法屬于Object類,作用是讓當(dāng)前執(zhí)行代碼的線程進(jìn)行等待,該方法用來將當(dāng)前線程置于"預(yù)執(zhí)行隊列"中,并且在wait()所在的代碼行處停止執(zhí)行,直到接到通知或者被中斷為止。在調(diào)用wait()方法之前,線程必須獲得該對象的對象級別鎖,只能在同步方法或者同步塊中調(diào)用wait()方法。在執(zhí)行wait()方法后,當(dāng)前線程釋放鎖。在從wait()返回前,線程與其他線程競爭重新獲得鎖。如果調(diào)用wait()時沒有持有適當(dāng)?shù)逆i,就會拋出IllegalMonitorStateException異常,它是RuntimeException的一個子類。因此不需要進(jìn)行異常捕獲。
此處有個面試題,是關(guān)于為什么wait()方法必須在同步中?占小狼的公眾號給出了答案,lost wake up問題,作者給出了生產(chǎn)和消費的模式距離來說明lost wake up問題,并給出要解決就必須同步,獲取同一對象鎖,連接如下:
阿里面試題,為什么wait()方法要放在同步塊中??mp.weixin.qq.com(2)notify()方法也需要在同步方法或者同步代碼塊中調(diào)用,在調(diào)用前,線程必須獲得該對象級別的鎖,如果調(diào)用notify()時沒有持有適當(dāng)?shù)逆i,就會拋出IllegalMonitorStateException異常。該方法用來通知那些可能等待對象的對象鎖的其他線程,如果有多個線程等待,則由線程規(guī)劃器隨機(jī)挑選其中一個呈wait狀態(tài)的線程,對其發(fā)出notify,并使它等待獲取該對象的對象鎖。需要說明的是,在執(zhí)行notify()方法后,當(dāng)前線程不會馬上釋放鎖,呈wait狀態(tài)的線程也不會馬上獲得鎖,必須等到notify()方法的線程執(zhí)行完,走出同步代碼塊或則方法的時候,當(dāng)前線程才會釋放鎖,wait狀態(tài)的線程才可以獲得鎖。
notify()方法可以隨機(jī)喚醒等待隊列中等待同一共享資源的一個線程,并使線程退出等待隊列,進(jìn)入可運行的狀態(tài),該方法一次只可以喚醒一個線程。
notifyAll()方法是可以喚醒所有正在等待隊列中等待同一共享資源的全部線程,讓其從等待狀態(tài)退出,進(jìn)入可運行狀態(tài),誰的優(yōu)先級高,誰將會先被執(zhí)行,也有可能隨機(jī)執(zhí)行,取決于JVM虛擬機(jī)的實現(xiàn)。
下面這段代碼,因為沒有“對象監(jiān)視器”,沒有同步鎖的原因,所以出現(xiàn)了異常
package異常:
Exception下面這段代碼,雖然使用了synchronized關(guān)鍵字,而且wait方法也在同步塊中,但是因為當(dāng)前線程main被掛起,一直處于等待,所以wait()方法后面的代碼都沒有執(zhí)行機(jī)會。
package運行結(jié)果:
sync下面這段代碼實現(xiàn)了線程間的通信,線程A先啟動執(zhí)行,然后調(diào)用wait方法,線程睡眠3秒,然后線程B啟動執(zhí)行,并執(zhí)行了notify()方法,通知喚醒wait()的線程,當(dāng)線程B執(zhí)行完synchronized同步代碼塊,然后釋放了對象鎖,wait()的線程獲取到了對象鎖,然后繼續(xù)執(zhí)行。
package運行結(jié)果:
begin下面這段代碼,是當(dāng)list中添加元素5個的時候,然后就notify()另外一個wait()的線程。
package運行結(jié)果:
wait從運行結(jié)果可以看出,最開始是wait begin,結(jié)束是wait end。
線程的生命周期:
大致分為創(chuàng)建、可運行、運行、阻塞、銷毀等五個狀態(tài)。
其中可運行狀態(tài)和運行狀態(tài)可以相互轉(zhuǎn)換,阻塞狀態(tài)和可運行狀態(tài)可以相互轉(zhuǎn)換。
線程進(jìn)入Runnable狀態(tài)的情況:
(1)調(diào)用sleep()方法后經(jīng)過的時間超過了指定的休眠時間
(2)線程調(diào)用的阻塞IO已經(jīng)返回,阻塞方法執(zhí)行完畢
(3)線程成功得獲得了試圖同步的監(jiān)視器
(4)線程正在等待某個通知,其他線程發(fā)出了通知
(5)處于掛起狀態(tài)的線程調(diào)用了resume方法恢復(fù)線程
線程出現(xiàn)阻塞狀態(tài)的情況:
(1)線程調(diào)用sleep方法,主動放棄占用的處理器資源
(2)線程調(diào)用了阻塞式IO方法,在方法返回前,該線程被阻塞
(3)線程試圖獲得一個同步監(jiān)視器,但是該同步監(jiān)視器正被其他線程所持有
(4)線程等待某個通知
(5)程序調(diào)用了suspend()方法,掛起該線程,此方法容易導(dǎo)致死鎖,應(yīng)該避免調(diào)用。
wait方法執(zhí)行后,鎖會自動釋放;notify()方法執(zhí)行后,鎖不會自動釋放,除非執(zhí)行完對應(yīng)的synchronized方法,才會釋放鎖;sleep方法也是不釋放鎖的(????sleep方法不是指定線程休眠時間,休眠時間過后,線程就i 重新獲得CPU,去執(zhí)行嗎????我的理解:多線程情況下,其中一個線程在同步塊中被指定休眠時間,這個線程是在休眠期間不會釋放鎖,在休眠結(jié)束,同步塊執(zhí)行完以后就會釋放對象鎖)。
執(zhí)行完同步代碼塊會釋放對象鎖;
在執(zhí)行同步代碼塊的過程中,如果遇到異常導(dǎo)致線程終止,鎖也會被釋放;
在執(zhí)行同步代碼塊的過程中,執(zhí)行了鎖所屬對象的wait()方法,這個線程會釋放對象鎖,而此線程對象會進(jìn)入線程等待池中,等待被喚醒。
wait(long)方法:等待某一時間內(nèi)是否有線程對鎖進(jìn)行喚醒,如果超過這個時間則自動喚醒。
下面代碼使用wait,分別有兩個線程執(zhí)行,都執(zhí)行了同步方法,從控制臺可以看出,程序依然沒有執(zhí)行完,正是因為A線程執(zhí)行了wait()方法后,釋放了對象鎖,B線程才可以獲取對象鎖,然后執(zhí)行同步代碼塊,執(zhí)行wait。
package運行結(jié)果:
begin下面的程序中使用了wait和notify結(jié)合,其中wait執(zhí)行完,其他線程還可以繼續(xù)執(zhí)行同步代碼塊,但是執(zhí)行到notify的時候,程序就一直不能結(jié)束,原因是notify()方法指定完后沒有釋放對象鎖,所以才導(dǎo)致這個問題發(fā)生,代碼和運行結(jié)果如下所示:
package begin當(dāng)interrupt()方法和wait()方法相遇,下面是示例代碼和運行結(jié)果:
package begin下面這段代碼是關(guān)于notify()方法,一次只可以喚醒一個線程(隨機(jī))的驗證:
package程序運行結(jié)果:
begin多線程中的生產(chǎn)者和消費者模式:
(1)一個生產(chǎn)者和一個消費者:操作值
package運行結(jié)果:set和get交替執(zhí)行
set(2)多生產(chǎn)者與多消費者:操作值:假死
下面的這段代碼,因為存在使用notify(),可能生產(chǎn)喚醒生產(chǎn)者,消費者喚醒消費者,所以會造成假死狀態(tài)。
package運行結(jié)果:
productor總結(jié)
以上是生活随笔為你收集整理的educoder 使用线程锁(lock)实现线程同步_线程间的通信(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 御馔津最新御魂搭配方法
- 下一篇: 王者荣耀李元芳皮肤什么时候返场