等待-通知機制:如果線程要求的條件不滿足,則線程阻塞自己,進入等待狀態(tài);當線程要求的條件滿足后,通知等待的線程重新執(zhí)行。
在上一節(jié)中使用??while(!actr.apply(this, target));? 語句來循環(huán)獲得鎖,這種循環(huán)等待的方式會浪費CPU資源,應該讓線程阻塞,等滿足要求再喚醒線程。這里不得不說一下這兩種方式的優(yōu)缺點:循環(huán)等待會浪費CPU的資源,但是如果等待-通知頻繁同樣會浪費資源。因為,線程在切換的時候,CPU需要把線程的上下文存儲到內(nèi)存,頻繁的切換同樣不能達到優(yōu)化的效果。
synchronized配合 wait()/notify()/notifyAll() 實現(xiàn)等待-通知機制
等待隊列和互斥鎖(synchronized 的對象即為互斥鎖的對象)是一對一的關(guān)系,每個互斥鎖都有自己獨立的等待隊列。

當條件不滿足時,當前線程會被wait()阻塞,同樣,線程釋放鎖,使得其他線程可以競爭獲取鎖。
而當條件滿足時,notify()或者notifyAll()通知被阻塞的隊列,可以重新從wait()的位置(注意是wait()的位置而不是像左側(cè)等待隊列一樣重新進入臨界區(qū))喚醒。喚醒線程仍需要重新獲取鎖。這里要注意notify() 只能保證在通知時間點,條件是滿足的。而被通知線程的執(zhí)行時間點和通知的時間點基本上不會重合,所以當線程執(zhí)行的時候,很可能條件已經(jīng)不滿足了。這也就是下面模板的由來

如果 synchronized 鎖定的是 this,那么對應的一定是 this.wait()、this.notify()、this.notifyAll();如果 synchronized 鎖定的是 target,那么對應的一定是 target.wait()、target.notify()、target.notifyAll()
notify() 和 notifyAll()的區(qū)別
notify() 是會隨機地通知等待隊列中的一個線程,而 notifyAll() 會通知等待隊列中的所有線程。盡量使用 notifyAll()。原因:當資源滿足要求,要喚醒所需要資源的線程,notify()通知的可能是一個不需要該資源的線程,也就浪費了這次機會,可能導致饑餓問題;而notifyAll()一定可以通知到需要該資源的線程,至于搶不搶的到是它的事情。
課后問題:wait() 方法和 sleep() 方法都能讓當前線程掛起一段時間,那它們的區(qū)別是什么?
回答:1:wait釋放資源,sleep不釋放資源;? ?2:wait需要被喚醒,sleep不需要;? 3:wait需要獲取到監(jiān)視器,否則拋異常,sleep不需要;? ?4:wait是object頂級父類的方法,sleep則是Thread的方法