解讀synchronized鎖的釋放機(jī)制
synchronized鎖的釋放機(jī)制
synchronized 鎖的釋放機(jī)制是通過 JVM 底層的 Monitor 鎖模型和 計(jì)數(shù)器機(jī)制 實(shí)現(xiàn)的。
以下是具體的釋放邏輯和觸發(fā)條件:
一、鎖釋放的觸發(fā)條件
正常執(zhí)行結(jié)束
當(dāng)線程執(zhí)行完 synchronized
修飾的代碼塊或方法時(shí),JVM 會(huì)自動(dòng)調(diào)用 monitorexit
指令釋放鎖。
- 同步代碼塊:通過
monitorenter
和monitorexit
指令顯式控制鎖的獲取與釋放。 - 同步方法:通過
ACC_SYNCHRONIZED
標(biāo)志隱式觸發(fā)鎖的獲取和釋放,方法結(jié)束時(shí)自動(dòng)釋放鎖。
異常退出
- 如果在
synchronized
代碼塊或方法中拋出未捕獲的異常,JVM 會(huì)執(zhí)行monitorexit
指令釋放鎖。 - 編譯時(shí)會(huì)為同步代碼塊生成兩個(gè)
monitorexit
指令:一個(gè)用于正常退出,另一個(gè)用于異常路徑。
顯式調(diào)用 wait()
- 線程在
synchronized
代碼塊內(nèi)調(diào)用wait()
方法時(shí) - 會(huì)暫時(shí)釋放鎖并進(jìn)入等待狀態(tài),直到其他線程調(diào)用
notify()
/notifyAll()
喚醒它
二、鎖釋放的核心機(jī)制
計(jì)數(shù)器遞減
- 每個(gè)對(duì)象關(guān)聯(lián)一個(gè) Monitor 鎖,內(nèi)部維護(hù)一個(gè)計(jì)數(shù)器(
_count
)。 - 線程每次進(jìn)入
synchronized
代碼塊時(shí)計(jì)數(shù)器加 1,退出時(shí)減 1。 - 當(dāng)計(jì)數(shù)器歸零時(shí),鎖完全釋放,其他線程可競(jìng)爭(zhēng)獲取。
Monitor 對(duì)象的狀態(tài)管理
- 持有鎖的線程(
_owner
):釋放鎖后,_owner
置為null
,計(jì)數(shù)器歸零。 - 等待隊(duì)列(
_EntryList
和_WaitSet
):鎖釋放后,JVM 會(huì)從_EntryList
或_WaitSet
中喚醒線程重新競(jìng)爭(zhēng)鎖。
三、不同場(chǎng)景的鎖釋放示例
1. 同步代碼塊
public void method() { synchronized (this) { // 代碼邏輯 } // 此處自動(dòng)執(zhí)行 monitorexit 釋放鎖 }
無論正常結(jié)束還是拋出異常,monitorexit
都會(huì)觸發(fā)鎖釋放。
2. 同步方法
public synchronized void method() { // 代碼邏輯 } // 方法結(jié)束自動(dòng)釋放鎖
通過 ACC_SYNCHRONIZED
標(biāo)志隱式管理鎖,無需顯式字節(jié)碼指令。
3. 異常場(chǎng)景
public void method() { synchronized (this) { throw new RuntimeException(); // 觸發(fā)異常,自動(dòng)釋放鎖 } }
即使未捕獲異常,JVM 也會(huì)執(zhí)行 monitorexit
指令釋放鎖。
四、鎖釋放的底層實(shí)現(xiàn)(字節(jié)碼層面)
同步代碼塊
編譯后生成 monitorenter
和兩個(gè) monitorexit
(正常退出和異常退出)指令:
public void method(); Code: 0: aload_0 1: dup 2: astore_1 3: monitorenter // 獲取鎖 4: ... // 業(yè)務(wù)代碼 13: monitorexit // 正常退出釋放鎖 14: goto 20 17: aload_1 18: monitorexit // 異常退出釋放鎖 19: athrow 20: return
同步方法
方法訪問標(biāo)志包含 ACC_SYNCHRONIZED
,JVM 在方法入口和出口隱式管理鎖。
五、注意事項(xiàng)
不會(huì)釋放鎖的操作
Thread.sleep()
、Thread.yield()
不會(huì)釋放鎖。- 線程掛起(如
suspend()
)也不會(huì)釋放鎖。
可重入性
- 同一線程可多次獲取鎖(計(jì)數(shù)器遞增)
- 需對(duì)應(yīng)次數(shù)的退出操作才能完全釋放
總結(jié)
synchronized 鎖的釋放依賴于 JVM 的 Monitor 模型和計(jì)數(shù)器機(jī)制,通過以下方式觸發(fā):
- 代碼塊或方法正常結(jié)束。
- 未捕獲異常拋出。
- 顯式調(diào)用
wait()
。 - 其底層通過
monitorexit
指令或隱式標(biāo)志確保鎖的正確釋放,保障線程安全。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
設(shè)置JavaScript自動(dòng)提示-Eclipse/MyEclipse
自動(dòng)提示需要2個(gè)組件,分別是:ext-4.0.2a.jsb2||spket-1.6.16.jar,需要的朋友可以參考下2016-05-05Java 中的vector和list的區(qū)別和使用實(shí)例詳解
在大家還沒有了解vector,list,deque的知識(shí)之前,我先給大家介紹下stl,本文重點(diǎn)給大家介紹vector和list的區(qū)別及使用,感興趣的的朋友一起看看吧2017-09-09詳解RocketMQ 消費(fèi)端如何監(jiān)聽消息
這篇文章主要為大家介紹了RocketMQ 消費(fèi)端如何監(jiān)聽消息示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12java+testng+selenium的自動(dòng)化測(cè)試實(shí)例
這篇文章主要介紹了java+testng+selenium的自動(dòng)化測(cè)試實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11Java8函數(shù)式接口的基礎(chǔ)學(xué)習(xí)教程
這篇文章主要給大家介紹了關(guān)于Java8函數(shù)式接口基礎(chǔ)學(xué)習(xí)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04