深入詳解Java中synchronized鎖升級(jí)的套路
synchronized鎖是啥?鎖其實(shí)就是一個(gè)對(duì)象,隨便哪一個(gè)都可以,Java中所有的對(duì)象都是鎖,換句話說(shuō),Java中所有對(duì)象都可以成為鎖。
這次我們主要聊的是synchronized鎖升級(jí)的套路
synchronized
會(huì)經(jīng)歷四個(gè)階段:無(wú)鎖狀態(tài)、偏向鎖、輕量級(jí)鎖、重量級(jí)鎖依次從耗費(fèi)資源最少,性能最高,到耗費(fèi)資源多,性能最差。
鎖原理
先看看這些狀態(tài)的鎖為什么稱之為鎖,他們的互斥原理是啥。
偏向鎖
當(dāng)一個(gè)線程到達(dá)同步代碼塊,嘗試獲取鎖對(duì)象的時(shí)候,會(huì)查看對(duì)象頭中的MarkWord
里的線程ID,如果這里沒(méi)有ID則將自己的保存進(jìn)去,拿到鎖。若是有,則查看是否是當(dāng)前線程,如果不是,就CAS嘗試改,如果是,就已經(jīng)拿到了鎖資源。
這里詳細(xì)說(shuō)說(shuō)CAS嘗試修改的邏輯:它會(huì)檢查持有偏向鎖的線程狀態(tài)。首先遍歷當(dāng)前JVM的所有存活的線程,如果能找到偏向的線程,則說(shuō)明偏向的線程還存活,此時(shí)會(huì)檢查線程是否在執(zhí)行同步代碼塊中的代碼,如果是,則升級(jí)為輕量級(jí)鎖,去繼續(xù)進(jìn)行CAS競(jìng)爭(zhēng)鎖。所以加了偏向鎖之后,同時(shí)只有一個(gè)線程可以拿到鎖執(zhí)行同步代碼塊中的代碼。
輕量級(jí)鎖
查看對(duì)象頭中的MarkWord
里的Lock Record
指針指向的是否是當(dāng)前線程的虛擬機(jī)棧,如果是,拿鎖執(zhí)行業(yè)務(wù),如果不是則進(jìn)行CAS,嘗試修改,若是修改幾次都沒(méi)有成功,再升級(jí)到重量級(jí)鎖。
重量級(jí)鎖
查看對(duì)象頭中的MarkWord
里的指向的ObjectMonitor
,查看owner是否是當(dāng)前線程,如果不是,扔到ObjectMonitor
里的EntryList
中排隊(duì),并掛起線程,等待被喚醒。
鎖升級(jí)
無(wú)鎖
一般情況下,新new出來(lái)的一個(gè)對(duì)象,暫時(shí)就是無(wú)鎖狀態(tài)。因?yàn)槠蜴i默認(rèn)是有延遲的,在啟動(dòng)JVM的前4s中,不存在偏向鎖,但是如果關(guān)閉了偏向鎖延遲的設(shè)置,new出來(lái)的對(duì)象,就會(huì)添加一個(gè)匿名偏向鎖。也就是說(shuō)這個(gè)對(duì)象想找一個(gè)線程去增加偏向鎖,但是沒(méi)有找到,稱之為匿名偏向。存儲(chǔ)的線程ID為一堆0000,也沒(méi)有任何地址信息。
我們可以通過(guò)以下配置關(guān)閉偏向鎖延遲。
//關(guān)閉偏向鎖延遲的指令 -XX:BiasedLockingStartuoDelay=0
偏向鎖
當(dāng)某一個(gè)線程來(lái)獲取這個(gè)鎖資源時(shí),此時(shí)會(huì)成功獲取到,就會(huì)變?yōu)槠蜴i,偏向鎖存儲(chǔ)線程的ID。
當(dāng)偏向鎖升級(jí)時(shí),會(huì)觸發(fā)偏向鎖撤銷,偏向鎖撤銷需要等到一個(gè)安全點(diǎn),比如GC的時(shí)候,偏向鎖撤銷的成本太高,所以默認(rèn)開(kāi)始時(shí),會(huì)做偏向鎖延遲。若是直接有多個(gè)線程競(jìng)爭(zhēng),會(huì)跳過(guò)偏向鎖,直接變?yōu)檩p量級(jí)鎖。
細(xì)說(shuō)一下偏向鎖撤銷的過(guò)程,成本為啥高呢?當(dāng)一個(gè)線程拿到偏向鎖之后,會(huì)把鎖的對(duì)象頭的Mark Work
中的線程id指向自己,當(dāng)又有一個(gè)線程來(lái)了進(jìn)行爭(zhēng)搶導(dǎo)致鎖升級(jí)的的時(shí)候,會(huì)暫停之前拿到偏向鎖的線程,然后清空Mark Work中的線程id,增加一個(gè)輕量級(jí)鎖,然后再恢復(fù)暫停的線程繼續(xù)執(zhí)行。這也是為什么等到安全點(diǎn)再執(zhí)行鎖升級(jí)的原因,因?yàn)橐獣和>€程。
常見(jiàn)的安全點(diǎn):
- 執(zhí)行GC的時(shí)候
- 方法返回之前
- 調(diào)用某個(gè)方法之后
- 拋出異常的位置
- 一個(gè)循環(huán)的末尾
輕量級(jí)鎖
當(dāng)在出現(xiàn)了多個(gè)線程的競(jìng)爭(zhēng),就會(huì)升級(jí)為輕量級(jí)鎖,輕量級(jí)鎖的效果就是基于CAS嘗試獲取鎖資源,這里會(huì)用到自適應(yīng)自旋鎖,根據(jù)上次CAS成功與否,耗費(fèi)的時(shí)間,決定這次自旋多少次。
輕量級(jí)鎖適用于競(jìng)爭(zhēng)不是很激烈的場(chǎng)景,一個(gè)線程拿到鎖,執(zhí)行同步代碼塊,很快就處理完了。再來(lái)一個(gè)線程嘗試一兩次也拿到了鎖,再去執(zhí)行,不會(huì)讓一個(gè)線程等待很久。
重量級(jí)鎖
如果到了重量級(jí)鎖,那就沒(méi)啥說(shuō)的了,如果有線程持有鎖,其他想拿鎖的就掛起,等待鎖釋放后被依次喚醒。
鎖粗化&鎖消除
鎖粗化/鎖膨脹
鎖膨脹是編譯Java文件的時(shí)候,JIT幫我們做的優(yōu)化,它會(huì)減少鎖的獲取和釋放次數(shù)。 比如:
while(){ synchronized(){ // 多次的獲取和釋放,成本太高,會(huì)被優(yōu)化為下面這種 } } synchronized(){ while(){ // 拿到鎖后執(zhí)行循環(huán),只加鎖和釋放一次 } }
鎖消除
鎖消除則是在一個(gè)加鎖的同步代碼塊中,沒(méi)有任何共享資源,也不存在鎖競(jìng)爭(zhēng)的情況,JIT編譯時(shí),就直接將鎖的指令優(yōu)化掉。 比如
synchronized(){ int a = 1; a++; //操作局部變量的邏輯 }
到此這篇關(guān)于深入詳解Java中synchronized鎖升級(jí)的套路的文章就介紹到這了,更多相關(guān)Java synchronized鎖升級(jí)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Maven方式構(gòu)建SpringBoot項(xiàng)目的實(shí)現(xiàn)步驟(圖文)
Maven是一個(gè)強(qiáng)大的項(xiàng)目管理工具,可以幫助您輕松地構(gòu)建和管理Spring Boot應(yīng)用程序,本文主要介紹了Maven方式構(gòu)建SpringBoot項(xiàng)目的實(shí)現(xiàn)步驟,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09Feign?日期格式轉(zhuǎn)換錯(cuò)誤的問(wèn)題
這篇文章主要介紹了Feign?日期格式轉(zhuǎn)換錯(cuò)誤的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03深入解析Java的設(shè)計(jì)模式編程中建造者模式的運(yùn)用
這篇文章主要介紹了深入解析Java的設(shè)計(jì)模式編程中建造者模式的運(yùn)用,同時(shí)文中也介紹了建造者模式與工廠模式的區(qū)別,需要的朋友可以參考下2016-02-02Java編程實(shí)現(xiàn)游戲中的簡(jiǎn)單碰撞檢測(cè)功能示例
這篇文章主要介紹了Java編程中的簡(jiǎn)單碰撞檢測(cè)功能,涉及java針對(duì)坐標(biāo)點(diǎn)的相關(guān)數(shù)學(xué)運(yùn)算操作技巧,需要的朋友可以參考下2017-10-10JAVA實(shí)現(xiàn)空間索引編碼——GeoHash的示例
本篇文章主要介紹了JAVA實(shí)現(xiàn)空間索引編碼——GeoHash的示例,如何從眾多的位置信息中查找到離自己最近的位置,有興趣的朋友可以了解一下2016-10-10怎樣將一個(gè)JAR包添加到Java應(yīng)用程序的Boot?Classpath中
本文文章給大家介紹如何將一個(gè)JAR包添加到Java應(yīng)用程序的Boot?Classpath中,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的的朋友參考下吧2023-11-11MyBatis處理mysql主鍵自動(dòng)增長(zhǎng)出現(xiàn)的不連續(xù)問(wèn)題解決
本文主要介紹了MyBatis處理mysql主鍵自動(dòng)增長(zhǎng)出現(xiàn)的不連續(xù)問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09使用spring動(dòng)態(tài)獲取接口的不同實(shí)現(xiàn)類
這篇文章主要介紹了使用spring動(dòng)態(tài)獲取接口的不同實(shí)現(xiàn)類,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02