摘要:前置知識(shí)點(diǎn)對(duì)象頭要了解鎖優(yōu)化策略中的輕量級(jí)鎖與偏向鎖的原理和運(yùn)作過(guò)程,需要先了解虛擬機(jī)的對(duì)象頭部分的內(nèi)存布局。否則說(shuō)明這個(gè)鎖對(duì)象已經(jīng)被其他線程搶占了。
前置知識(shí)點(diǎn):對(duì)象頭
要了解鎖優(yōu)化策略中的輕量級(jí)鎖與偏向鎖的原理和運(yùn)作過(guò)程,需要先了解Hotspot虛擬機(jī)的對(duì)象頭部分的內(nèi)存布局。
對(duì)象頭(摘自《深入理解java虛擬機(jī)》)對(duì)象頭信息是與對(duì)象自身定義的數(shù)據(jù)無(wú)關(guān)的額外存儲(chǔ)成本
如果對(duì)象是數(shù)組類型,則虛擬機(jī)用3個(gè)Word(字寬,在32位虛擬機(jī)中,一字寬等于四字節(jié),即32bit)存儲(chǔ)對(duì)象頭。如果對(duì)象是非數(shù)組類型,則用2Word存儲(chǔ)對(duì)象頭。一個(gè)額外的字寬用于存儲(chǔ)數(shù)組長(zhǎng)度。
第一個(gè)字寬,用來(lái)存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù) 如:哈希嗎(HashCode)、GC分代年齡(Generational GC Age)等,這部分?jǐn)?shù)據(jù)的長(zhǎng)度在32位和64位的虛擬機(jī)中分別為32bit和64bit,簡(jiǎn)稱“Mark Word”。
第二個(gè)字寬用于存儲(chǔ)指向方法區(qū)對(duì)象類型數(shù)據(jù)的指針。
對(duì)象頭信息會(huì)根據(jù)對(duì)象的狀態(tài)復(fù)用自己的存儲(chǔ)空間。例如:在32位的HotSpot虛擬機(jī)中對(duì)象未被鎖定的狀態(tài)下,Mark Word的32bit空間中的25bit用于存儲(chǔ)對(duì)象哈希嗎(HashCode),4bit用于存儲(chǔ)對(duì)象分代年齡,2bit用于存儲(chǔ)鎖標(biāo)志位,1bit固定為0。
整個(gè)對(duì)象頭如下圖:第三行即為第三部分,非數(shù)組類型的沒(méi)有第三個(gè)字寬。
Mark Word的默認(rèn)存儲(chǔ)結(jié)構(gòu)如下圖:
在運(yùn)行期間Mark Word里存儲(chǔ)的數(shù)據(jù)會(huì)隨著鎖標(biāo)志位的變化而變化。Mark Word可能變化為存儲(chǔ)以下4種數(shù)據(jù):
在64位虛擬機(jī)下,Mark Word是64bit大小的,其存儲(chǔ)結(jié)構(gòu)如下:
鎖狀態(tài)Java SE1.6為了減少獲得鎖和釋放鎖所帶來(lái)的性能消耗,引入了“偏向鎖”和“輕量級(jí)鎖”,
所以在Java SE1.6里鎖一共有四種狀態(tài),無(wú)鎖狀態(tài),偏向鎖狀態(tài),輕量級(jí)鎖狀態(tài)和重量級(jí)鎖狀態(tài),它會(huì)隨著競(jìng)爭(zhēng)情況逐漸升級(jí)。
鎖可以升級(jí)但不能降級(jí),意味著偏向鎖升級(jí)成輕量級(jí)鎖后不能降級(jí)成偏向鎖。
這種鎖升級(jí)卻不能降級(jí)的策略,目的是為了提高獲得鎖和釋放鎖的效率,下文會(huì)詳細(xì)分析。
偏向鎖Hotspot的作者經(jīng)過(guò)以往的研究發(fā)現(xiàn)大多數(shù)情況下鎖不僅不存在多線程競(jìng)爭(zhēng),而且總是由同一線程多次獲得,為了讓線程獲得鎖的代價(jià)更低而引入了偏向鎖。
在沒(méi)有實(shí)際競(jìng)爭(zhēng)的情況下,還能夠針對(duì)部分場(chǎng)景繼續(xù)優(yōu)化。如果不僅僅沒(méi)有實(shí)際競(jìng)爭(zhēng),自始至終,使用鎖的線程都只有一個(gè),那么,維護(hù)輕量級(jí)鎖都是浪費(fèi)的。偏向鎖的目標(biāo)是,減少無(wú)競(jìng)爭(zhēng)且只有一個(gè)線程使用鎖的情況下,使用輕量級(jí)鎖產(chǎn)生的性能消耗。輕量級(jí)鎖每次申請(qǐng)、釋放鎖都至少需要一次CAS,但偏向鎖只有初始化時(shí)需要一次CAS。
配置默認(rèn)-XX:+UseBiasedLocking=true
-XX:-UseBiasedLocking=false關(guān)閉偏向鎖
應(yīng)用程序啟動(dòng)幾秒鐘之后才激活
-XX:BiasedLockingStartupDelay = 0關(guān)閉延遲
根據(jù)上圖,我們可以看到,首先判斷鎖對(duì)象是不是可偏向?qū)ο螅?strong>若鎖對(duì)象已經(jīng)被輕量級(jí)鎖定或者重量級(jí)鎖定了,因?yàn)殒i不會(huì)降級(jí),所以它是不可偏向,同樣的,關(guān)閉了偏向鎖的設(shè)置-UseBiasedLocking=false,也會(huì)造成鎖對(duì)象不可偏向)
接下來(lái),我們假定鎖對(duì)象處于可偏向狀態(tài),并且ThreadID為0即biasable & unbiased狀態(tài)(這里不討論epoch和age)
當(dāng)一個(gè)線程試圖鎖住一個(gè)處于biasable & unbiased狀態(tài)的對(duì)象時(shí),通過(guò)一個(gè)CAS將自己的ThreadID放置到Mark Word中相應(yīng)的位置,如果CAS操作成功進(jìn)入第(3)步否則進(jìn)入(4)步
當(dāng)進(jìn)入到這一步時(shí)代表當(dāng)前沒(méi)有鎖競(jìng)爭(zhēng),鎖對(duì)象繼續(xù)保持biasable可偏向狀態(tài),但是這時(shí)ThreadID字段被設(shè)置成了偏向鎖所有者的ID,然后進(jìn)入到第(6)步
當(dāng)前線程執(zhí)行CAS獲取偏向鎖失敗(這一步是偏向鎖的關(guān)鍵),表示在該鎖對(duì)象上存在競(jìng)爭(zhēng)并且這個(gè)時(shí)候另外一個(gè)線程在持有偏向鎖所有權(quán)。這個(gè)時(shí)候,就要判斷持有偏向鎖的線程是否還活著,因?yàn)橐粋€(gè)線程執(zhí)行完同步代碼塊后,不會(huì)主動(dòng)釋放偏向鎖。如果持有偏向鎖的線程還活著,將偏向鎖消除,膨脹為輕量級(jí)鎖,否則,將偏向鎖消除,讓爭(zhēng)鎖的線程持有偏向鎖。
具體過(guò)程是:當(dāng)?shù)竭_(dá)全局安全點(diǎn)(safepoint,在這個(gè)時(shí)間點(diǎn)上沒(méi)有字節(jié)碼正在執(zhí)行)時(shí)擁有偏向鎖的線程被掛起,然后檢查持有偏向鎖的線程是否活著,如果線程不處于活動(dòng)狀態(tài),則將對(duì)象頭設(shè)置成無(wú)鎖狀態(tài),如果線程仍然活著,鎖對(duì)象有可能升級(jí)為輕量級(jí)鎖狀態(tài)(鎖標(biāo)志位置為00),阻塞在安全點(diǎn)的原持有線程被釋放,進(jìn)入到輕量級(jí)鎖的執(zhí)行路徑中,繼續(xù)往下執(zhí)行同步代碼。
當(dāng)一個(gè)線程試圖鎖住一個(gè)處于biasable & biased并且ThreadID不等于自己的ID時(shí),這時(shí)由于存在鎖競(jìng)爭(zhēng)必須進(jìn)入到第(4)步來(lái)撤銷偏向鎖。
運(yùn)行同步代碼塊
輕量級(jí)鎖輕量級(jí)鎖是由偏向所升級(jí)來(lái)的,偏向鎖運(yùn)行在一個(gè)線程進(jìn)入同步塊的情況下,當(dāng)?shù)诙€(gè)線程加入鎖爭(zhēng)用的時(shí)候,偏向鎖就會(huì)升級(jí)為輕量級(jí)鎖;
加鎖過(guò)程在代碼進(jìn)入同步塊的時(shí)候,如果此同步對(duì)象沒(méi)有被鎖定(鎖標(biāo)志位為“01”狀態(tài)),虛擬機(jī)首先將在當(dāng)前線程的棧幀中建立一個(gè)名為鎖記錄(Lock Record)的空間,用于存儲(chǔ)鎖記錄目前的Mark Word的拷貝(稱為Displaced Mark Word)以及記錄鎖對(duì)象的指針owner。
當(dāng)一個(gè)線程來(lái)獲取這個(gè)鎖,虛擬機(jī)將使用CAS操作嘗試將鎖對(duì)象的Mark Word更新為指向Lock Record的指針。如果這個(gè)更新動(dòng)作成功了,那么這個(gè)線程就擁有了該對(duì)象的鎖,并且對(duì)象Mark Word的鎖標(biāo)志位(Mark Word的最后2bit)將轉(zhuǎn)變?yōu)椤?0”,即表示此對(duì)象處于輕量級(jí)鎖定狀態(tài),這時(shí)候線程堆棧與對(duì)象頭的狀態(tài)如下:
如果這個(gè)更新操作失敗了,虛擬機(jī)首先會(huì)檢查對(duì)象的Mark Word是否指向當(dāng)前線程的棧幀。如果指向,說(shuō)明當(dāng)前線程已經(jīng)擁有了這個(gè)對(duì)象的鎖,那就可以直接進(jìn)入同步塊繼續(xù)執(zhí)行。
否則說(shuō)明這個(gè)鎖對(duì)象已經(jīng)被其他線程搶占了。那么它就會(huì)自旋等待鎖,一定次數(shù)后仍未獲得鎖對(duì)象,則修改mark word,將其修改為重量級(jí)鎖的指針,表示該鎖對(duì)象進(jìn)入了重量鎖狀態(tài)。如果有兩條以上的線程爭(zhēng)用同一個(gè)鎖,那輕量級(jí)鎖就不再有效,要膨脹為重量級(jí)鎖,鎖標(biāo)志的狀態(tài)值變?yōu)椤?0”,Mark Word中存儲(chǔ)的就是指向重量級(jí)(互斥量)的指針。
由輕量鎖切換到重量鎖,是發(fā)生在輕量鎖釋放鎖的期間的,另一邊,持有輕量級(jí)鎖的線程,之前在獲取鎖的時(shí)候它拷貝了鎖對(duì)象頭的mark word,在釋放鎖的時(shí)候如果它發(fā)現(xiàn)在它持有鎖的期間有其他線程來(lái)嘗試獲取鎖了,并且該線程對(duì)mark word做了修改,兩者比對(duì)發(fā)現(xiàn)不一致導(dǎo)致釋放鎖的CAS失敗,于是也切換到重量鎖,釋放輕量鎖,并喚醒阻塞的線程。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/71559.html
摘要:這兩種策略的區(qū)別就在于,公平策略會(huì)讓等待時(shí)間長(zhǎng)的線程優(yōu)先執(zhí)行,非公平策略則是等待時(shí)間長(zhǎng)的線程不一定會(huì)執(zhí)行,存在一個(gè)搶占資源的問(wèn)題。 之前有一篇文章我們簡(jiǎn)單的談到了Java中同步的問(wèn)題,但是可能在平常的開(kāi)發(fā)中,有些理論甚至是某些方式是用不到的,但是從程序的角度看,這些理論思想我們可以運(yùn)用到我們的開(kāi)發(fā)中,比如是不是應(yīng)該一談到同步問(wèn)題,就應(yīng)該想到用synchronized?,什么時(shí)候應(yīng)該用R...
摘要:關(guān)于,最后有兩點(diǎn)規(guī)律需要注意當(dāng)?shù)牡却?duì)列隊(duì)首結(jié)點(diǎn)是共享結(jié)點(diǎn),說(shuō)明當(dāng)前寫鎖被占用,當(dāng)寫鎖釋放時(shí),會(huì)以傳播的方式喚醒頭結(jié)點(diǎn)之后緊鄰的各個(gè)共享結(jié)點(diǎn)。當(dāng)?shù)牡却?duì)列隊(duì)首結(jié)點(diǎn)是獨(dú)占結(jié)點(diǎn),說(shuō)明當(dāng)前讀鎖被使用,當(dāng)讀鎖釋放歸零后,會(huì)喚醒隊(duì)首的獨(dú)占結(jié)點(diǎn)。 showImg(https://segmentfault.com/img/remote/1460000016012293); 本文首發(fā)于一世流云的專欄:...
摘要:自選鎖鎖膨脹后,虛擬機(jī)為了避免線程真實(shí)地在操作系統(tǒng)層面掛起,虛擬機(jī)還會(huì)在做最后的努力自選鎖。 showImg(https://segmentfault.com/img/remote/1460000016159660?w=500&h=333); 作為一款公用平臺(tái),JDK 本身也為并發(fā)程序的性能絞盡腦汁,在 JDK 內(nèi)部也想盡一切辦法提供并發(fā)時(shí)的系統(tǒng)吞吐量。這里,我將向大家簡(jiǎn)單介紹幾種 J...
摘要:限期阻塞調(diào)用方法等待時(shí)間結(jié)束或線程執(zhí)行完畢。終止?fàn)顟B(tài)線程執(zhí)行完畢或出現(xiàn)異常退了。和都會(huì)檢查線程何時(shí)中斷,并且在發(fā)現(xiàn)中斷時(shí)提前放回。工廠方法將線程池的最大大小設(shè)置為,而將基本大小設(shè)置為,并將超時(shí)大小設(shè)置為分鐘。 wait()、notify()、notifyAll() Object是所有類的基類,它有5個(gè)方法組成了等待、通知機(jī)制的核心:notify()、notifyAll()、wait()...
摘要:以下為大家整理了阿里巴巴史上最全的面試題,涉及大量面試知識(shí)點(diǎn)和相關(guān)試題。的內(nèi)存結(jié)構(gòu),和比例。多線程多線程的幾種實(shí)現(xiàn)方式,什么是線程安全。點(diǎn)擊這里有一套答案版的多線程試題。線上系統(tǒng)突然變得異常緩慢,你如何查找問(wèn)題。 以下為大家整理了阿里巴巴史上最全的 Java 面試題,涉及大量 Java 面試知識(shí)點(diǎn)和相關(guān)試題。 JAVA基礎(chǔ) JAVA中的幾種基本數(shù)據(jù)類型是什么,各自占用多少字節(jié)。 S...
閱讀 2676·2021-11-25 09:43
閱讀 2484·2021-09-22 15:29
閱讀 1000·2021-09-22 15:17
閱讀 3640·2021-09-03 10:36
閱讀 2236·2019-08-30 13:54
閱讀 1757·2019-08-30 11:23
閱讀 1171·2019-08-29 16:58
閱讀 1301·2019-08-29 16:14