詳解JVM系列之對象的鎖狀態(tài)和同步
java對象頭
Java的鎖狀態(tài)其實可以分為三種,分別是偏向鎖,輕量級鎖和重量級鎖。
在Java HotSpot VM中,每個對象前面都有一個class指針和一個Mark Word。 Mark Word存儲了哈希值以及分代年齡和標記位等,通過這些值的變化,JVM可以實現(xiàn)對java對象的不同程度的鎖定。
還記得我們之前分享java對象的那張圖嗎?
javaObject對象的對象頭大小根據(jù)你使用的是32位還是64位的虛擬機的不同,稍有變化。這里我們使用的是64位的虛擬機為例。
Object的對象頭,分為兩部分,第一部分是Mark Word,用來存儲對象的運行時數(shù)據(jù)比如:hashcode,GC分代年齡,鎖狀態(tài),持有鎖信息,偏向鎖的thread ID等等。
在64位的虛擬機中,Mark Word是64bits,如果是在32位的虛擬機中Mark Word是32bits。
第二部分就是Klass Word,Klass Word是一個類型指針,指向class的元數(shù)據(jù),JVM通過Klass Word來判斷該對象是哪個class的實例。
我們可以看到對象頭中的Mark Word根據(jù)狀態(tài)的不同,存儲的是不同的內(nèi)容。
其中鎖標記的值分別是:無鎖=001,偏向鎖=101,輕量級鎖=000,重量級鎖=010。
java中鎖狀態(tài)的變化
為什么java中的鎖有三種狀態(tài)呢?其本質原因是為了提升鎖的效率,因為不同情況下,鎖的力度是不一樣的。
通過設置不同的鎖的狀態(tài),從而可以不同的情況用不同的處理方式。
下圖是java中的鎖狀態(tài)的變化圖:
上面的圖基本上列出了java中鎖狀態(tài)的整個生命周期。接下來我們一個一個的講解。
偏向鎖biased locking
一般來說,一個對象被一個線程獲得鎖之后,很少發(fā)生線程切換的情況。也就是說大部分情況下,一個對象只是被一個對象鎖定的。
那么這個時候我們可以通過設置Mark word的一定結構,減少使用CAS來更新對象頭的頻率。
為了實現(xiàn)這樣的目標,我們看下偏向鎖的Mark word的結構:
當偏向線程第一次進入同步塊的時候,會去判斷偏向鎖的狀態(tài)和thread ID,如果偏向鎖狀態(tài)是1,并且thread ID是空的話,將會使用CAS命令來更新對象的Mark word。
設置是否偏向鎖=1,鎖標記=01,線程ID設置為當前鎖定該對象的線程。
下一次該對象進入同步塊的時候,會先去判斷鎖定的線程ID和當前線程ID是否相等,如果相等的話則不需要執(zhí)行CAS命令,直接進入同步塊。
如果這個時候有第二個線程想訪問該對象的同步塊,因為當前對象頭的thread ID是第一個線程的ID,跟第二個線程的ID不同。
如果這個時候線程1的同步塊已經(jīng)執(zhí)行完畢,那么需要解除偏向鎖的鎖定。
解除鎖定很簡單,就是將線程ID設置為空,并且將偏向鎖的標志位設為0,
如果這個時候線程1的同步塊還在執(zhí)行,那么需要將偏向鎖升級為輕量級鎖。
輕量級鎖thin lock
先看下輕量級鎖的結構:
可以看到Mark word中存放的是棧中鎖記錄的指針和鎖的標記=00。
如果對象現(xiàn)在處于未加鎖狀態(tài),當一個線程嘗試進入同步塊的時候,會將把對象頭和當前對象的指針拷貝一份,放在線程的棧中一個叫做lock record的地方。
然后JVM通過CAS操作,將對象頭中的指針指向剛剛拷貝的lock record。如果成功,則該線程擁有該對象的鎖。
實際上Lock Record和Mark word形成了一個互相指向對方的情況。
下次這個線程再次進入同步塊的時候,同樣執(zhí)行CAS,比較Mark word中的指針是否和當前thread的lock record地址一致,如果一致表明是同一個線程,可以繼續(xù)持有該鎖。
如果這個時候有第二個線程,也想進入該對象的同步塊,也會執(zhí)行CAS操作,很明顯會失敗,因為對象頭中的指針和lock record的地址不一樣。
這個時候第二個線程就會自旋等待。
那么第一個線程什么時候會釋放鎖呢?
輕量級鎖在線程退出同步塊的時候,同樣需要執(zhí)行CAS命令,將鎖標記從00替換成01,也就是無鎖狀態(tài)。
重量級鎖
如果第二個線程自旋時間太久,就會將鎖標記替換成10(重量級鎖),并且設置重量級鎖的指針,指向第二個線程,然后進入阻塞狀態(tài)。
當?shù)谝粋€線程退出同步塊的時候,執(zhí)行CAS命令就會出錯,這時候第一個線程就知道鎖已經(jīng)膨脹成為重量級鎖了。
第一個線程就會釋放鎖,并且喚醒等待的第二個線程。
第二個線程被喚醒之后,重新爭奪鎖。
我們看下重量級鎖的結構:
三種鎖狀態(tài)的不同
偏向鎖,輕量級鎖和重量級鎖到底有什么不同了?
這里總結一下,偏向鎖下次進入的時候不需要執(zhí)行CAS命令,只做線程ID的比較即可。
輕量級鎖進入和退出同步塊都需要執(zhí)行CAS命令,但是輕量級鎖不會阻塞,它使用的是自旋命令來獲取鎖。
重量級鎖不使用自旋,但是會阻塞線程。!
以上就是詳解JVM系列之對象的鎖狀態(tài)和同步的詳細內(nèi)容,更多關于JVM系列之對象的鎖狀態(tài)和同步的資料請關注腳本之家其它相關文章!
相關文章
SpringCloud-Alibaba-Sentinel服務降級,熱點限流,服務熔斷
這篇文章主要介紹了SpringCloud-Alibaba-Sentinel服務降級,熱點限流,服務熔斷,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12詳解Spring Cloud 斷路器集群監(jiān)控(Turbine)
這篇文章主要介紹了詳解Spring Cloud 斷路器集群監(jiān)控(Turbine),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05SpringBoot使用classfinal-maven-plugin插件加密Jar包的示例代碼
這篇文章給大家介紹了SpringBoot使用classfinal-maven-plugin插件加密Jar包的實例,文中通過代碼示例和圖文講解的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下2024-02-02解決spring懶加載以及@PostConstruct結合的坑
這篇文章主要介紹了解決spring懶加載以及@PostConstruct結合的坑,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12