简单分析synchronized不会锁泄漏的原因
最近看到一句話:內部鎖synchronized不會造成鎖泄漏(Lock Leak)。
鎖泄漏是指一個線程獲得某個鎖以后,由于程序的錯誤、缺陷致使該鎖一直沒法被釋放而導致其他線程一直無法獲得該鎖的現象。(摘自《Java多線程編程實戰指南(核心篇)》--黃文海)
很好奇JVM是怎么保證的。
我想,Java代碼,最終無非是編譯成字節碼,變成一條條指令,或許可以從指令入手研究一下。
1. 一個小例子
我們先來看看下面的代碼。
1 public class SynchronizedTest { 2 3 private static final Object LOCK = new Object(); 4 5 public static void main(String[] args) { 6 synchronized (LOCK) { 7 foo(); 8 } 9 } 10 11 public static void foo() { 12 //do something... 13 } 14 }很簡單的一段代碼,在臨界區調用了一個方法。
通過javap -c SynchronizedTest.class ,得到如下信息,重點關注紅框部分:
?
第5和第10行分別是monitorenter 和 monitorexit,這表示進入和退出臨界區,臨界區中間第6行調用了foo()這個方法。當退出臨界區后,第11行直接goto第19行,即return,從當前方法返回,方法結束。
這是正常情況下的執行順序。
那么如果在臨界區中發生了異常(本例中是foo()可能發生異常),執行順序又是怎么樣的呢?
2. 異常表
在研究執行順序之前,先學習一下異常表。
異常表實際上是Java代碼的一部分,編譯器使用異常表而不是簡單的跳轉命令來實現Java異常及finally處理機制。
上圖的 Exception table即為異常表。它列舉了哪些代碼行的范圍內,可能出現某種異常時,對應的處理機制。
【
那么異常表用在什么時候呢?
答案是異常發生的時候,當一個異常發生時
1.JVM會在當前出現異常的方法中,查找異常表,是否有合適的處理者來處理
2.如果當前方法異常表不為空,并且異常符合處理者的from和to節點,并且type也匹配,則JVM調用位于target的調用者來處理。
3.如果上一條未找到合理的處理者,則繼續查找異常表中的剩余條目
4.如果當前方法的異常表無法處理,則向上查找(彈棧處理)剛剛調用該方法的調用處,并重復上面的操作。
5.如果所有的棧幀被彈出,仍然沒有處理,則拋給當前的Thread,Thread則會終止。
6.如果當前Thread為最后一個非守護線程,且未處理異常,則會導致JVM終止運行。
以上就是JVM處理異常的一些機制。
】--以上摘自?詳解JVM如何處理異常
具體到本例中
1. 當臨界區發生異常,JVM查找異常表,發現符合第一行記錄(異常發生點在6和11行(不包括第11行)之間,任意異常類型),于是跳到第14行,即第10行的monitorexit要么不執行,要么執行拋出異常,總之無法成功執行。
2. 從14行一路執行下去,到16行,再次monitorexit,此時釋放鎖。18行拋出上面獲取到的異常,19行從當前方法返回void, 結束方法的運行。
3. 如果第2步過程中又發生異常,根據異常表第二行記錄(異常點發生在14和17行(不包括第17行)之間,任意異常類型),會再次跳到14行,重新執行第2步,循環直到monitorexit指令成功執行。
這樣就保證了monitorexit一定能夠執行成功,鎖一定會被釋放。
轉載于:https://www.cnblogs.com/kingsleylam/p/10982104.html
總結
以上是生活随笔為你收集整理的简单分析synchronized不会锁泄漏的原因的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JS中的DOM
- 下一篇: CSS实现垂直居中的常用方法