Synchronize原理

1.對象存儲布局


對象頭:Java對象頭一般占有2個機器碼(在32位虛擬機中,1個機器碼等于4字節(jié),也就是32bit,在64位虛擬機中,1個機器碼是8個字節(jié),也就是64bit)。如果對象是數(shù)組類型,則需要3個機器碼,用第三個機器碼來記錄數(shù)組長度。
實例數(shù)據(jù):存放類的屬性數(shù)據(jù)信息,包括父類的屬性信息。
對齊填充:由于虛擬機要求?對象起始地址必須是8字節(jié)的整數(shù)倍。填充數(shù)據(jù)不是必須存在的,僅僅是為了字節(jié)對齊。


2.對象頭

對象頭結(jié)構(gòu)

對象頭由三部分組成:Mark Word(標記字段),Class Pointer(類型指針)和數(shù)組長度,三個部分分別占用一個機器碼大小。
Mark Word:用于存儲對象自身的運行時數(shù)據(jù),包括哈希碼(HashCode),GC分代年齡,鎖狀態(tài)標志,線程持有的鎖,偏向線程 ID,偏向時間戳等。
Class Pointer:對象指向它的class類元數(shù)據(jù)的指針,能夠通過這個指針來確定對象是哪個類的實例。
Array length:存儲數(shù)組的長度,只有數(shù)組對象才有該字段。

3.Mark Word(標記字段)

32位JVM無鎖狀態(tài)


32位JVM有鎖狀態(tài)

Mark Word被設(shè)計成一個非固定的數(shù)據(jù)結(jié)構(gòu)以便在極小的空間內(nèi)存存儲盡量多的數(shù)據(jù),它會根據(jù)對象的狀態(tài)復(fù)用自己的存儲空間。即Mark Word會隨著程序的運行發(fā)生變化,可能變化為存儲以上四種數(shù)據(jù)。注意不管數(shù)據(jù)結(jié)構(gòu)怎么變,最后的兩個bit都是鎖標志位。

4.鎖的狀態(tài)

鎖主要存在四種狀態(tài):無鎖、偏向鎖、輕量級鎖、重量級鎖。鎖可以從偏向鎖升級到輕量級鎖,再升級的重量級鎖。但是鎖的升級是單向的,也就是說只能從低到高升級,不會出現(xiàn)鎖的降級。

偏向鎖:在大多數(shù)情況下,鎖不僅不存在多線程競爭,而且總是由同一線程多次獲得,為了讓線程獲得鎖的代價更低,引進了偏向鎖。當(dāng)一個線程訪問同步塊并獲取鎖時,會在對象頭和棧幀中的鎖記錄里存儲鎖偏向的線程ID,以后該線程進入和退出同步塊時不需要花費CAS操作來爭奪鎖資源,只需要檢查是否為偏向鎖、鎖標識為以及ThreadID即可。

輕量級鎖:依據(jù)是?“對于絕大部分的鎖,在整個生命周期內(nèi)都是不會存在競爭的”。

重量級鎖:通過對象內(nèi)部的一個叫做?監(jiān)視器鎖(Monitor)來實現(xiàn)的。但是監(jiān)視器鎖本質(zhì)又是依賴于底層的操作系統(tǒng)的Mutex Lock來實現(xiàn)的。而操作系統(tǒng)實現(xiàn)線程之間的切換這就需要從用戶態(tài)轉(zhuǎn)換到核心態(tài),這個成本非常高,狀態(tài)之間的轉(zhuǎn)換需要相對比較長的時間,這就是為什么Synchronized效率低的原因。因此,這種依賴于操作系統(tǒng)Mutex Lock所實現(xiàn)的鎖我們稱之為?“重量級鎖”。

對比總結(jié):如果是單線程使用,那偏向鎖毫無疑問代價最小,并且它就能解決問題,連CAS都不用做,僅僅在內(nèi)存中比較下對象頭就可以了。如果出現(xiàn)了其他線程競爭,則偏向鎖就會升級為輕量級鎖。如果其他線程通過一定次數(shù)的CAS嘗試沒有成功,則進入重量級鎖。假如遇到鎖升級,同步代碼塊就要做偏向鎖建立、偏向鎖撤銷、輕量級鎖建立、升級到重量級鎖,最終還是得靠重量級鎖來解決問題,那這樣的代價就比直接用重量級鎖要大不少了。

鎖對比


5.Synchronized的用法

Synchronized可以把任何一個非null對象作為"鎖",有個專門的名字叫對象監(jiān)視器(Object Monitor)??偣灿腥N用法:
1.當(dāng)synchronized作用在實例方法時,監(jiān)視器鎖(monitor)便是對象實例(this)。
2.當(dāng)synchronized作用在靜態(tài)方法時,監(jiān)視器鎖(monitor)便是對象的Class實例,因為Class數(shù)據(jù)存在于永久代,因此靜態(tài)方法鎖相當(dāng)于該類的一個全局鎖。
3.當(dāng)synchronized作用在某一個對象實例時,監(jiān)視器鎖(monitor)便是括號括起來的對象實例。


6.synchronized的實現(xiàn)

Synchronized關(guān)鍵字反編譯后,主要是在同步代碼塊的前后分別調(diào)用了monitorEnter 和monitorExit兩個方法。即進入同步代碼塊之前會調(diào)用monitorEnter 指令,退出同步代碼塊會執(zhí)行monitorExit指令。發(fā)生異常退出也會執(zhí)行monitorExit指令,JVM會保證這兩個指令成對出現(xiàn)。
JVM解釋器執(zhí)行monitorEnter指令時,實際上時進入native方法調(diào)用,這里會根據(jù)對象頭的狀態(tài),使用偏向鎖,輕量級鎖或者重量級鎖去競爭共享對象,同時在此處也會根據(jù)競爭的情況把鎖進行膨脹升級。


7.JVM對鎖的優(yōu)化

鎖消除和鎖粗化

鎖消除:JVM檢測到不可能存在共享數(shù)據(jù)競爭,此時JVM會對這些同步鎖進行鎖消除。append操作是加鎖的,但buffer變量沒有逃逸,不存在多線程共享,因此可以直接消除append操作的鎖。
鎖粗化:JVM檢測到對同一個對象(buffer)連續(xù)加鎖、解鎖操作,會合并一個更大范圍的加鎖、解鎖操作,即加鎖解鎖操作會移到for循環(huán)之外。
鎖升級:Synchronized關(guān)鍵字在JDK1.5之前只使用重量級鎖,在JDK1.6之后引入了偏向鎖和輕量級鎖。會根據(jù)對象頭的狀態(tài),選擇使用偏向鎖,輕量級鎖和重量級鎖。同時也會動態(tài)的升級鎖(即膨脹,把對象頭的鎖逐步升級,偏向鎖->輕量級鎖->重量級鎖)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容