今天正好碰到這個問題,也疑惑了很久。看了一圈知乎上的答案,感受沒說到根上。因此本身又好好Google了一下,終於找到了讓本身信服的解釋。html
先說兩個概念:鎖池和等待池java
- 鎖池:假設線程A已經擁有了某個對象(注意:不是類)的鎖,而其它的線程想要調用這個對象的某個synchronized方法(或者synchronized塊),因爲這些線程在進入對象的synchronized方法以前必須先得到該對象的鎖的擁有權,可是該對象的鎖目前正被線程A擁有,因此這些線程就進入了該對象的鎖池中。
- 等待池:假設一個線程A調用了某個對象的wait()方法,線程A就會釋放該對象的鎖後,進入到了該對象的等待池中
- Reference:java中的鎖池和等待池
而後再來講notify和notifyAll的區別.net
- 若是線程調用了對象的 wait()方法,那麼線程便會處於該對象的等待池中,等待池中的線程不會去競爭該對象的鎖。
- 當有線程調用了對象的 notifyAll()方法(喚醒全部 wait 線程)或 notify()方法(只隨機喚醒一個 wait 線程),被喚醒的的線程便會進入該對象的鎖池中,鎖池中的線程會去競爭該對象鎖。也就是說,調用了notify後只要一個線程會由等待池進入鎖池,而notifyAll會將該對象等待池內的全部線程移動到鎖池中,等待鎖競爭。
- 優先級高的線程競爭到對象鎖的機率大,倘若某線程沒有競爭到該對象鎖,它還會留在鎖池中,惟有線程再次調用 wait()方法,它纔會從新回到等待池中。而競爭到對象鎖的線程則繼續往下執行,直到執行完了 synchronized 代碼塊,它會釋放掉該對象鎖,這時鎖池中的線程會繼續競爭該對象鎖。
- Reference:線程間協做:wait、notify、notifyAll
綜上,所謂喚醒線程,另外一種解釋能夠說是將線程由等待池移動到鎖池,notifyAll調用後,會將所有線程由等待池移到鎖池,而後參與鎖的競爭,競爭成功則繼續執行,若是不成功則留在鎖池等待鎖被釋放後再次參與競爭。而notify只會喚醒一個線程。線程
有了這些理論基礎,後面的notify可能會致使死鎖,而notifyAll則不會的例子也就好解釋了htm
順便說下:sleep和wait的區別:對象
對於sleep()方法,該方法是屬於Thread類中的。而wait()方法,則是屬於Object類中的。blog
sleep()方法致使了程序暫停執行指定的時間,讓出cpu該其餘線程,可是他的監控狀態依然保持者,當指定的時間到了又會自動恢復運行狀態(能夠理解爲抱着鎖睡覺...)。get
在調用sleep()方法的過程當中,線程不會釋放對象鎖。it
而當調用wait()方法的時候,線程會放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象調用notify()方法後本線程才進入對象鎖定池準備(扔掉鎖去睡覺...)。io