先說兩個概念:鎖池和等待池.net
鎖池:假設線程A已經擁有了某個對象(注意:不是類)的鎖,而其它的線程想要調用這個對象的某個synchronized方法(或者synchronized塊),因爲這些線程在進入對象的synchronized方法以前必須先得到該對象的鎖的擁有權,可是該對象的鎖目前正被線程A擁有,因此這些線程就進入了該對象的鎖池中。
等待池:假設一個線程A調用了某個對象的wait()方法,線程A就會釋放該對象的鎖後,進入到了該對象的等待池中。線程
而後再來講notify和notifyAll的區別對象
若是線程調用了對象的 wait()方法,那麼線程便會處於該對象的等待池中,等待池中的線程不會去競爭該對象的鎖。
當有線程調用了對象的 notifyAll()方法(喚醒全部 wait 線程)或 notify()方法(只隨機喚醒一個 wait 線程),被喚醒的的線程便會進入該對象的鎖池中,鎖池中的線程會去競爭該對象鎖。也就是說,調用了notify後只要一個線程會由等待池進入鎖池,而notifyAll會將該對象等待池內的全部線程移動到鎖池中,等待鎖競爭
優先級高的線程競爭到對象鎖的機率大,倘若某線程沒有競爭到該對象鎖,它還會留在鎖池中,惟有線程再次調用 wait()方法,它纔會從新回到等待池中。而競爭到對象鎖的線程則繼續往下執行,直到執行完了 synchronized 代碼塊,它會釋放掉該對象鎖,這時鎖池中的線程會繼續競爭該對象鎖。blog
綜上,所謂喚醒線程,另外一種解釋能夠說是將線程由等待池移動到鎖池,notifyAll調用後,會將所有線程由等待池移到鎖池,而後參與鎖的競爭,競爭成功則繼續執行,若是不成功則留在鎖池等待鎖被釋放後再次參與競爭。而notify只會喚醒一個線程。隊列
有了這些理論基礎,後面的notify可能會致使死鎖,而notifyAll則不會的例子也就好解釋了進程
https://blog.csdn.net/djzhao/article/details/79410229開發
==========同步
sleep()是使線程暫停執行一段時間的方法。wait()也是一種使線程暫停執行的方法。例如,當線程執行wait()方法時候,會釋放當前的鎖,而後讓出CPU,進入等待狀態。而且能夠調用notify()方法或者notifyAll()方法通知正在等待的其餘線程。notify()方法僅喚醒一個線程(等待隊列中的第一個線程)並容許他去得到鎖。notifyAll()方法喚醒全部等待這個對象的線程並容許他們去競爭得到鎖。具體區別以下:it
1) 原理不一樣。sleep()方法是Thread類的靜態方法,是線程用來控制自身流程的,他會使此線程暫停執行一段時間,而把執行機會讓給其餘線程,等到計時時間一到,此線程會自動甦醒。例如,當線程執行報時功能時,每一秒鐘打印出一個時間,那麼此時就須要在打印方法前面加一個sleep()方法,以便讓本身每隔一秒執行一次,該過程如同鬧鐘同樣。而wait()方法是object類的方法,用於線程間通訊,這個方法會使當前擁有該對象鎖的進程等待,直到其餘線程調用notify()方法或者notifyAll()時才醒來,不過開發人員也能夠給他指定一個時間,自動醒來。io
2) 對鎖的 處理機制不一樣。因爲sleep()方法的主要做用是讓線程暫停執行一段時間,時間一到則自動恢復,不涉及線程間的通訊,所以,調用sleep()方法並不會釋放鎖。而wait()方法則不一樣,當調用wait()方法後,線程會釋放掉他所佔用的鎖,從而使線程所在對象中的其餘synchronized數據能夠被其餘線程使用。
3) 使用區域不一樣。wait()方法必須放在同步控制方法和同步代碼塊中使用,sleep()方法則能夠放在任何地方使用。sleep()方法必須捕獲異常,而wait()、notify()、notifyAll()不須要捕獲異常。在sleep的過程當中,有可能被其餘對象調用他的interrupt(),產生InterruptedException。因爲sleep不會釋放鎖標誌,容易致使死鎖問題的發生,所以通常狀況下,推薦使用wait()方法。————————————————版權聲明:本文爲CSDN博主「種向日葵的小仙女」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。原文連接:https://blog.csdn.net/qiuchaoxi/article/details/79837568