溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

jvm細(xì)節(jié)探索之synchronized的示例分析

發(fā)布時(shí)間:2021-08-06 10:55:12 來源:億速云 閱讀:135 作者:小新 欄目:編程語言

這篇文章主要介紹了jvm細(xì)節(jié)探索之synchronized的示例分析,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

在C程序代碼中我們可以利用操作系統(tǒng)提供的互斥鎖來實(shí)現(xiàn)同步塊的互斥訪問及線程的阻塞及喚醒等工作。然而在Java中除了提供LockAPI外還在語法層面上提供了synchronized關(guān)鍵字來實(shí)現(xiàn)互斥同步原語。那么到底在JVM內(nèi)部是怎么實(shí)現(xiàn)synchronized關(guān)鍵子的呢?

一、synchronized的字節(jié)碼表示:

在java語言中存在兩種內(nèi)建的synchronized語法:1、synchronized語句;2、synchronized方法。對(duì)于synchronized語句當(dāng)Java源代碼被javac編譯成bytecode的時(shí)候,會(huì)在同步塊的入口位置和退出位置分別插入monitorenter和monitorexit字節(jié)碼指令。而synchronized方法則會(huì)被翻譯成普通的方法調(diào)用和返回指令如:invokevirtual、areturn指令,在VM字節(jié)碼層面并沒有任何特別的指令來實(shí)現(xiàn)被synchronized修飾的方法,而是在Class文件的方法表中將該方法的access_flags字段中的synchronized標(biāo)志位置1,表示該方法是同步方法并使用調(diào)用該方法的對(duì)象或該方法所屬的Class在JVM的內(nèi)部對(duì)象表示Klass做為鎖對(duì)象。

二、JVM中鎖的優(yōu)化:

簡(jiǎn)單來說在JVM中monitorenter和monitorexit字節(jié)碼依賴于底層的操作系統(tǒng)的MutexLock來實(shí)現(xiàn)的,但是由于使用MutexLock需要將當(dāng)前線程掛起并從用戶態(tài)切換到內(nèi)核態(tài)來執(zhí)行,這種切換的代價(jià)是非常昂貴的;然而在現(xiàn)實(shí)中的大部分情況下,同步方法是運(yùn)行在單線程環(huán)境(無鎖競(jìng)爭(zhēng)環(huán)境)如果每次都調(diào)用MutexLock那么將嚴(yán)重的影響程序的性能。不過在jdk1.6中對(duì)鎖的實(shí)現(xiàn)引入了大量的優(yōu)化,如鎖粗化(LockCoarsening)、鎖消除(LockElimination)、輕量級(jí)鎖(LightweightLocking)、偏向鎖(BiasedLocking)、適應(yīng)性自旋(AdaptiveSpinning)等技術(shù)來減少鎖操作的開銷。

鎖粗化(LockCoarsening):也就是減少不必要的緊連在一起的unlock,lock操作,將多個(gè)連續(xù)的鎖擴(kuò)展成一個(gè)范圍更大的鎖。

鎖消除(LockElimination):通過運(yùn)行時(shí)JIT編譯器的逃逸分析來消除一些沒有在當(dāng)前同步塊以外被其他線程共享的數(shù)據(jù)的鎖保護(hù),通過逃逸分析也可以在線程本地Stack上進(jìn)行對(duì)象空間的分配(同時(shí)還可以減少Heap上的垃圾收集開銷)。

輕量級(jí)鎖(LightweightLocking):這種鎖實(shí)現(xiàn)的背后基于這樣一種假設(shè),即在真實(shí)的情況下我們程序中的大部分同步代碼一般都處于無鎖競(jìng)爭(zhēng)狀態(tài)(即單線程執(zhí)行環(huán)境),在無鎖競(jìng)爭(zhēng)的情況下完全可以避免調(diào)用操作系統(tǒng)層面的重量級(jí)互斥鎖,取而代之的是在monitorenter和monitorexit中只需要依靠一條CAS原子指令就可以完成鎖的獲取及釋放。當(dāng)存在鎖競(jìng)爭(zhēng)的情況下,執(zhí)行CAS指令失敗的線程將調(diào)用操作系統(tǒng)互斥鎖進(jìn)入到阻塞狀態(tài),當(dāng)鎖被釋放的時(shí)候被喚醒(具體處理步驟下面詳細(xì)討論)。

偏向鎖(BiasedLocking):是為了在無鎖競(jìng)爭(zhēng)的情況下避免在鎖獲取過程中執(zhí)行不必要的CAS原子指令,因?yàn)镃AS原子指令雖然相對(duì)于重量級(jí)鎖來說開銷比較小但還是存在非常可觀的本地延遲(可參考這篇文章)。

適應(yīng)性自旋(AdaptiveSpinning):當(dāng)線程在獲取輕量級(jí)鎖的過程中執(zhí)行CAS操作失敗時(shí),在進(jìn)入與monitor相關(guān)聯(lián)的操作系統(tǒng)重量級(jí)鎖(mutexsemaphore)前會(huì)進(jìn)入忙等待(Spinning)然后再次嘗試,當(dāng)嘗試一定的次數(shù)后如果仍然沒有成功則調(diào)用與該monitor關(guān)聯(lián)的semaphore(即互斥鎖)進(jìn)入到阻塞狀態(tài)。

三、對(duì)象頭(ObjectHeader):

jvm細(xì)節(jié)探索之synchronized的示例分析

在JVM中創(chuàng)建對(duì)象時(shí)會(huì)在對(duì)象前面加上兩個(gè)字大小的對(duì)象頭,在32位機(jī)器上一個(gè)字為32bit,根據(jù)不同的狀態(tài)位MarkWorld中存放不同的內(nèi)容,如上圖所示在輕量級(jí)鎖中,MarkWord被分成兩部分,剛開始時(shí)LockWord為被設(shè)置為HashCode、最低三位表示LockWord所處的狀態(tài),初始狀態(tài)為001表示無鎖狀態(tài)。Klassptr指向Class字節(jié)碼在虛擬機(jī)內(nèi)部的對(duì)象表示的地址。Fields表示連續(xù)的對(duì)象實(shí)例字段。

四、MonitorRecord:

MonitorRecord是線程私有的數(shù)據(jù)結(jié)構(gòu),每一個(gè)線程都有一個(gè)可用monitorrecord列表,同時(shí)還有一個(gè)全局的可用列表;那么這些monitorrecord有什么用呢?每一個(gè)被鎖住的對(duì)象都會(huì)和一個(gè)monitorrecord關(guān)聯(lián)(對(duì)象頭中的LockWord指向monitorrecord的起始地址,由于這個(gè)地址是8byte對(duì)齊的所以LockWord的最低三位可以用來作為狀態(tài)位),同時(shí)monitorrecord中有一個(gè)Owner字段存放擁有該鎖的線程的唯一標(biāo)識(shí),表示該鎖被這個(gè)線程占用。如下圖所示為MonitorRecord的內(nèi)部結(jié)構(gòu):

jvm細(xì)節(jié)探索之synchronized的示例分析

Owner:初始時(shí)為NULL表示當(dāng)前沒有任何線程擁有該monitor record,當(dāng)線程成功擁有該鎖后保存線程唯一標(biāo)識(shí),當(dāng)鎖被釋放時(shí)又設(shè)置為NULL;

EntryQ:關(guān)聯(lián)一個(gè)系統(tǒng)互斥鎖(semaphore),阻塞所有試圖鎖住monitor record失敗的線程。

RcThis:表示blocked或waiting在該monitor record上的所有線程的個(gè)數(shù)。

Nest:用來實(shí)現(xiàn)重入鎖的計(jì)數(shù)。

HashCode:保存從對(duì)象頭拷貝過來的HashCode值(可能還包含GC age)。

Candidate:用來避免不必要的阻塞或等待線程喚醒,因?yàn)槊恳淮沃挥幸粋€(gè)線程能夠成功擁有鎖,如果每次前一個(gè)釋放鎖的線程喚醒所有正在阻塞或等待的線程,會(huì)引起不必要的上下文切換(從阻塞到就緒然后因?yàn)楦?jìng)爭(zhēng)鎖失敗又被阻塞)從而導(dǎo)致性能嚴(yán)重下降。Candidate只有兩種可能的值0表示沒有需要喚醒的線程1表示要喚醒一個(gè)繼任線程來競(jìng)爭(zhēng)鎖。

五、輕量級(jí)鎖具體實(shí)現(xiàn):

一個(gè)線程能夠通過兩種方式鎖住一個(gè)對(duì)象:1、通過膨脹一個(gè)處于無鎖狀態(tài)(狀態(tài)位001)的對(duì)象獲得該對(duì)象的鎖;2、對(duì)象已經(jīng)處于膨脹狀態(tài)(狀態(tài)位00)但LockWord指向的monitor record的Owner字段為NULL,則可以直接通過CAS原子指令嘗試將Owner設(shè)置為自己的標(biāo)識(shí)來獲得鎖。

獲取鎖(monitorenter)的大概過程如下:

(1)當(dāng)對(duì)象處于無鎖狀態(tài)時(shí)(RecordWord值為HashCode,狀態(tài)位為001),線程首先從自己的可用moniter record列表中取得一個(gè)空閑的moniter record,初始Nest和Owner值分別被預(yù)先設(shè)置為1和該線程自己的標(biāo)識(shí),一旦monitor record準(zhǔn)備好然后我們通過CAS原子指令安裝該monitor record的起始地址到對(duì)象頭的LockWord字段來膨脹(原文為inflate,我覺得之所以叫inflate主要是由于當(dāng)對(duì)象被膨脹后擴(kuò)展了對(duì)象的大?。粸榱丝臻g效率,將monitor record結(jié)構(gòu)從對(duì)象頭中抽出去,當(dāng)需要的時(shí)候才將該結(jié)構(gòu)attach到對(duì)象上,但是和這篇Paper有點(diǎn)互相矛盾,兩種實(shí)現(xiàn)方式稍微有點(diǎn)不同)該對(duì)象,如果存在其他線程競(jìng)爭(zhēng)鎖的情況而調(diào)用CAS失敗,則只需要簡(jiǎn)單的回到monitorenter重新開始獲取鎖的過程即可。

(2)對(duì)象已經(jīng)被膨脹同時(shí)Owner中保存的線程標(biāo)識(shí)為獲取鎖的線程自己,這就是重入(reentrant)鎖的情況,只需要簡(jiǎn)單的將Nest加1即可。不需要任何原子操作,效率非常高。

(3)對(duì)象已膨脹但Owner的值為NULL,當(dāng)一個(gè)鎖上存在阻塞或等待的線程同時(shí)鎖的前一個(gè)擁有者剛釋放鎖時(shí)會(huì)出現(xiàn)這種狀態(tài),此時(shí)多個(gè)線程通過CAS原子指令在多線程競(jìng)爭(zhēng)狀態(tài)下試圖將Owner設(shè)置為自己的標(biāo)識(shí)來獲得鎖,競(jìng)爭(zhēng)失敗的線程在則會(huì)進(jìn)入到第四種情況(4)的執(zhí)行路徑。

(4)對(duì)象處于膨脹狀態(tài)同時(shí)Owner不為NULL(被鎖住),在調(diào)用操作系統(tǒng)的重量級(jí)的互斥鎖之前先自旋一定的次數(shù),當(dāng)達(dá)到一定的次數(shù)時(shí)如果仍然沒有成功獲得鎖,則開始準(zhǔn)備進(jìn)入阻塞狀態(tài),首先將rfThis的值原子性的加1,由于在加1的過程中可能會(huì)被其他線程破壞Object和monitor record之間的關(guān)聯(lián),所以在原子性加1后需要再進(jìn)行一次比較以確保LockWord的值沒有被改變,當(dāng)發(fā)現(xiàn)被改變后則要重新進(jìn)行monitorenter過程。同時(shí)再一次觀察Owner是否為NULL,如果是則調(diào)用CAS參與競(jìng)爭(zhēng)鎖,鎖競(jìng)爭(zhēng)失敗則進(jìn)入到阻塞狀態(tài)。

釋放鎖(monitorexit)的大概過程如下:

(1)首先檢查該對(duì)象是否處于膨脹狀態(tài)并且該線程是這個(gè)鎖的擁有者,如果發(fā)現(xiàn)不對(duì)則拋出異常;

(2)檢查Nest字段是否大于1,如果大于1則簡(jiǎn)單的將Nest減1并繼續(xù)擁有鎖,如果等于1,則進(jìn)入到第(3)步;

(3)檢查rfThis是否大于0,設(shè)置Owner為NULL然后喚醒一個(gè)正在阻塞或等待的線程再一次試圖獲取鎖,如果等于0則進(jìn)入到第(4)步

(4)縮?。╠eflate)一個(gè)對(duì)象,通過將對(duì)象的LockWord置換回原來的HashCode值來解除和monitor record之間的關(guān)聯(lián)來釋放鎖,同時(shí)將monitor record放回到線程是有的可用monitor record列表。

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“jvm細(xì)節(jié)探索之synchronized的示例分析”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI