關(guān)鍵字
鎖通信
wait
notify
我們知道java線程一共有6種狀態(tài)砰识,分別是NEW
柱搜、RUNABLE
脂信、BLOCKED
癣蟋、WAITING
、TIMED_WATING
狰闪、TERMINATED
疯搅。而線程之前的狀態(tài)切換,其中一個(gè)方式就是靠wait()埋泵、notify()機(jī)制實(shí)現(xiàn)幔欧。
wait/notify機(jī)制
類似一種生產(chǎn)者-消費(fèi)者模型。 當(dāng)一個(gè)線程在獲取到資源的鎖后丽声,發(fā)現(xiàn)該資源不滿足使用條件礁蔗,就會(huì)調(diào)用wait()
方法,等待該資源滿足條件后再繼續(xù)執(zhí)行雁社。 而notify()
則類似生產(chǎn)者浴井,如果資源滿足條件了,則通過調(diào)用notify()霉撵,隨機(jī)的喚醒一個(gè)之前等待的線程磺浙,通知其條件已滿足,可以繼續(xù)執(zhí)行徒坡。
定義
wait()
notify()
是Object類中的兩個(gè)本地方法撕氧。對(duì)于wait()方法,解釋如下:
我們假設(shè)一個(gè)資源為object崭参。如果一個(gè)線程調(diào)用了資源object的wait()方法呵曹,那么將會(huì)發(fā)生如下事情:
- 線程進(jìn)入到object的
等待隊(duì)列(wait set)
,使自身由runable變?yōu)榱藈aiting狀態(tài)何暮。 - 釋放持有的object鎖奄喂。
- 等待其他線程調(diào)用object的notify、notifyAll方法才會(huì)被再次喚醒海洼。
喚醒之后跨新,線程則又變?yōu)榭烧{(diào)度狀態(tài),然后需要同其他線程再次去競爭object資源的鎖坏逢。一旦獲取到鎖之后域帐,線程就會(huì)完全恢復(fù)赘被,繼續(xù)從object.wait()
之后的代碼開始執(zhí)行。
由于我們一般是因?yàn)椴粷M足某些條件肖揣,才會(huì)調(diào)用wait方法等待民假,因此在線程恢復(fù)執(zhí)行后,我們總是應(yīng)該再次去check一下條件是否滿足龙优。這是因?yàn)檫@樣羊异,我們總是在一個(gè)循環(huán)中觸發(fā)wait()方法,類似:
synchronized (obj) {
while (<條件不滿足>)
obj.wait(timeout);
... // 條件已滿足彤断,開始執(zhí)行相應(yīng)的業(yè)務(wù)邏輯
}
notify方法則是用來從object的等待隊(duì)列里野舶,隨機(jī)的喚醒一個(gè)線程。被喚醒的線程再去競爭獲取object的鎖宰衙,然后繼續(xù)執(zhí)行平道。
整個(gè)運(yùn)作機(jī)制可以簡單用下圖來表示:
說明
無論是wait()還是notify(),其執(zhí)行的前提條件都是:該線程首先持有資源對(duì)象object的鎖供炼。否則的話一屋,就會(huì)拋出IllegalMonitorStateException異常。
wait(long timeout)
:和wait()方法類似的還有一個(gè)wait(long timeout)袋哼,如果線程一直沒有被喚醒陆淀,當(dāng)超時(shí)時(shí)間到了,就會(huì)自動(dòng)被喚醒先嬉。
notifyAll()
:notify()方法轧苫,也對(duì)應(yīng)有一個(gè)notifyAll()方法,該方法會(huì)將等待隊(duì)列里的所有線程都喚醒疫蔓,喚醒后的線程行為和notify()保持一致含懊。
事實(shí)上一個(gè)線程被喚醒的方式包括以下幾種:
- 其他線程調(diào)用object的notify()方法, 而該線程剛好被從等待隊(duì)列中選中.
- 其他線程調(diào)用了notifyAll()方法衅胀。
- 其他線程通過interrupt中斷了該線程岔乔。
- 等待超時(shí)時(shí)間到達(dá),線程被自動(dòng)喚醒滚躯。
剛剛上文也提到了執(zhí)行notify和wait雏门,必須先獲取到資源對(duì)象的鎖,獲取鎖的方式有三種:
- 執(zhí)行對(duì)象的同步方法掸掏。
- 執(zhí)行對(duì)象的同步代碼塊茁影。
- 對(duì)于類對(duì)象,執(zhí)行類的靜態(tài)同步方法丧凤。
參考文獻(xiàn)
http://www.reibang.com/p/9ac697c166f3
http://www.reibang.com/p/1dafbf42cc54