等待-通知機(jī)制:如果線程要求的條件不滿足,則線程阻塞自己,進(jìn)入等待狀態(tài)无畔;當(dāng)線程要求的條件滿足后恭理,通知等待的線程重新執(zhí)行涯保。
在上一節(jié)中使用??while(!actr.apply(this, target));? 語(yǔ)句來(lái)循環(huán)獲得鎖,這種循環(huán)等待的方式會(huì)浪費(fèi)CPU資源,應(yīng)該讓線程阻塞速侈,等滿足要求再喚醒線程。這里不得不說(shuō)一下這兩種方式的優(yōu)缺點(diǎn):循環(huán)等待會(huì)浪費(fèi)CPU的資源捅僵,但是如果等待-通知頻繁同樣會(huì)浪費(fèi)資源。因?yàn)椋€程在切換的時(shí)候窜司,CPU需要把線程的上下文存儲(chǔ)到內(nèi)存议薪,頻繁的切換同樣不能達(dá)到優(yōu)化的效果。
synchronized配合 wait()/notify()/notifyAll() 實(shí)現(xiàn)等待-通知機(jī)制
等待隊(duì)列和互斥鎖(synchronized 的對(duì)象即為互斥鎖的對(duì)象)是一對(duì)一的關(guān)系醇锚,每個(gè)互斥鎖都有自己獨(dú)立的等待隊(duì)列哼御。
當(dāng)條件不滿足時(shí)恋昼,當(dāng)前線程會(huì)被wait()阻塞,同樣,線程釋放鎖,使得其他線程可以競(jìng)爭(zhēng)獲取鎖空幻。
而當(dāng)條件滿足時(shí),notify()或者notifyAll()通知被阻塞的隊(duì)列,可以重新從wait()的位置(注意是wait()的位置而不是像左側(cè)等待隊(duì)列一樣重新進(jìn)入臨界區(qū))喚醒绽快。喚醒線程仍需要重新獲取鎖芥丧。這里要注意notify() 只能保證在通知時(shí)間點(diǎn),條件是滿足的活孩。而被通知線程的執(zhí)行時(shí)間點(diǎn)和通知的時(shí)間點(diǎn)基本上不會(huì)重合物遇,所以當(dāng)線程執(zhí)行的時(shí)候,很可能條件已經(jīng)不滿足了憾儒。這也就是下面模板的由來(lái)
如果 synchronized 鎖定的是 this,那么對(duì)應(yīng)的一定是 this.wait()阳掐、this.notify()始衅、this.notifyAll();如果 synchronized 鎖定的是 target缭保,那么對(duì)應(yīng)的一定是 target.wait()、target.notify()蝙茶、target.notifyAll()
notify() 和 notifyAll()的區(qū)別
notify() 是會(huì)隨機(jī)地通知等待隊(duì)列中的一個(gè)線程艺骂,而 notifyAll() 會(huì)通知等待隊(duì)列中的所有線程。盡量使用 notifyAll()隆夯。原因:當(dāng)資源滿足要求钳恕,要喚醒所需要資源的線程,notify()通知的可能是一個(gè)不需要該資源的線程蹄衷,也就浪費(fèi)了這次機(jī)會(huì)忧额,可能導(dǎo)致饑餓問題;而notifyAll()一定可以通知到需要該資源的線程愧口,至于搶不搶的到是它的事情睦番。
課后問題:wait() 方法和 sleep() 方法都能讓當(dāng)前線程掛起一段時(shí)間,那它們的區(qū)別是什么?
回答:1:wait釋放資源托嚣,sleep不釋放資源巩检;? ?2:wait需要被喚醒,sleep不需要示启;? 3:wait需要獲取到監(jiān)視器兢哭,否則拋異常,sleep不需要夫嗓;? ?4:wait是object頂級(jí)父類的方法迟螺,sleep則是Thread的方法