Java并发编程—notify和notifyAll有什么区别?
原文作者:知乎用戶
原文地址:https://www.zhihu.com/question/37601861/answer/145545371
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
今天正好碰到這個問題,也疑惑了好久。看了一圈知乎上的答案,感覺沒說到根上。所以自己又好好Google了一下,終于找到了讓自己信服的解釋。
先說兩個概念:鎖池和等待池
- 鎖池:假設線程A已經擁有了某個對象(注意:不是類)的鎖,而其它的線程想要調用這個對象的某個synchronized方法(或者synchronized塊),由于這些線程在進入對象的synchronized方法之前必須先獲得該對象的鎖的擁有權,但是該對象的鎖目前正被線程A擁有,所以這些線程就進入了該對象的鎖池中(鎖池就是AQS隊列)。
- 等待池:假設一個線程A調用了某個對象的wait()方法,線程A就會釋放該對象的鎖后,進入到了該對象的等待池中(等待池就是WaitQueue隊列))
Reference:
- java中的鎖池和等待池
- Java并發編程—線程間協作方式wait()/notify()/notifyAll()原理分析
然后再來說notify和notifyAll的區別
- 如果線程調用了對象的 wait()方法,那么線程便會處于該對象的等待池中,等待池中的線程不會去競爭該對象的鎖。
- 當有線程調用了對象的 notifyAll()方法(喚醒所有 wait 線程)或 notify()方法(只隨機喚醒一個 wait 線程),被喚醒的的線程便會進入該對象的鎖池中,鎖池中的線程會去競爭該對象鎖。也就是說,調用了notify后只要一個線程會由等待池進入鎖池,而notifyAll會將該對象等待池內的所有線程移動到鎖池中,等待鎖競爭
- 優先級高的線程競爭到對象鎖的概率大,假若某線程沒有競爭到該對象鎖,它還會留在鎖池中,唯有線程再次調用 wait()方法,它才會重新回到等待池中。而競爭到對象鎖的線程則繼續往下執行,直到執行完了 synchronized 代碼塊,它會釋放掉該對象鎖,這時鎖池中的線程會繼續競爭該對象鎖。
綜上,所謂喚醒線程,另一種解釋可以說是將線程由等待池移動到鎖池,notifyAll調用后,會將全部線程由等待池移到鎖池,然后參與鎖的競爭,競爭成功則繼續執行,如果不成功則留在鎖池等待鎖被釋放后再次參與競爭。而notify只會喚醒一個線程。有了這些理論基礎,后面的notify可能會導致死鎖,而notifyAll則不會的例子也就好解釋了。上述過程的圖示如下:
?
notify方法和notifyAll()方法兩者非常相似,到底該用哪一個,老實說,這個選擇有點困難。選擇notify的話,因為要喚醒的線程比較少(only one),程序的處理速度當然比notifyAll略勝一籌。但是選擇notify時,若這部分處理不好,可能會出現程序掛掉的危險。一般說來,選擇notifyAll所寫出來的程序代碼會比notify可靠。除非你能確定程序員對程序代碼的意義和能力限度一清二楚,否則選擇notifyAll應該是比較穩扎穩打。
總結
以上是生活随笔為你收集整理的Java并发编程—notify和notifyAll有什么区别?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java并发编程—说说Runnable与
- 下一篇: Java并发编程—ThreadLocal