synchronized的鎖升級(jí)過(guò)程

1.用法

synchronized可用來(lái)給對(duì)象和方法或者代碼塊加鎖傲武,當(dāng)它鎖定一個(gè)方法或者一個(gè)代碼塊的時(shí)候,同一時(shí)刻最多只有一個(gè)線(xiàn)程執(zhí)行這段代碼曹宴。

synchronized有三種應(yīng)用方式:

  1. 作用于實(shí)例方法搂橙,當(dāng)前實(shí)例加鎖,進(jìn)入同步代碼前要獲得當(dāng)前實(shí)例的鎖笛坦;
  2. 作用于靜態(tài)方法区转,當(dāng)前類(lèi)加鎖,進(jìn)去同步代碼前要獲得當(dāng)前類(lèi)對(duì)象的鎖弯屈;
  3. 作用于代碼塊蜗帜,對(duì)括號(hào)里配置的對(duì)象加鎖。

2.實(shí)現(xiàn)原理

2.1 Java對(duì)象頭

synchronized用的鎖存在Java對(duì)象頭里资厉,Java對(duì)象頭里的Mark Word默認(rèn)存儲(chǔ)對(duì)象的HashCode厅缺、分代年齡和鎖標(biāo)記位。在運(yùn)行期間宴偿,Mark Word里存儲(chǔ)的數(shù)據(jù)會(huì)隨著鎖標(biāo)志位的變化而變化湘捎。32位JVM的Mark Word可能變化存儲(chǔ)為以下5種數(shù)據(jù):


鎖一共有四種狀態(tài),級(jí)別從低到高依次是:無(wú)鎖狀態(tài)窄刘、偏向鎖狀態(tài)窥妇、輕量級(jí)鎖狀態(tài)和重量級(jí)鎖狀態(tài),這幾個(gè)狀態(tài)隨著競(jìng)爭(zhēng)情況逐漸升級(jí)娩践。為了提高獲得鎖和釋放鎖的效率活翩,鎖可以升級(jí)但不能降級(jí),意味著偏向鎖升級(jí)為輕量級(jí)鎖后不能降級(jí)為偏向鎖翻伺。


偏向鎖

當(dāng)我們創(chuàng)建一個(gè)對(duì)象時(shí)材泄,該對(duì)象的部分Markword關(guān)鍵數(shù)據(jù)如下。

從圖中可以看出吨岭,偏向鎖的標(biāo)志位是“01”拉宗,狀態(tài)是“0”,表示該對(duì)象還沒(méi)有被加上偏向鎖。(“1”是表示被加上偏向鎖)旦事。該對(duì)象被創(chuàng)建出來(lái)的那一刻魁巩,就有了偏向鎖的標(biāo)志位,這也說(shuō)明了所有對(duì)象都是可偏向的姐浮,但所有對(duì)象的狀態(tài)都為“0”谷遂,也同時(shí)說(shuō)明所有被創(chuàng)建的對(duì)象的偏向鎖并沒(méi)有生效。

不過(guò)单料,當(dāng)線(xiàn)程執(zhí)行到臨界區(qū)(critical section)時(shí)埋凯,此時(shí)會(huì)利用CAS(Compare and Swap)操作,將線(xiàn)程ID插入到Markword中扫尖,同時(shí)修改偏向鎖的標(biāo)志位白对。

所謂臨界區(qū),就是只允許一個(gè)線(xiàn)程進(jìn)去執(zhí)行操作的區(qū)域换怖,即同步代碼塊甩恼。CAS是一個(gè)原子性操作

此時(shí)的Mark word的結(jié)構(gòu)信息如下:

此時(shí)偏向鎖的狀態(tài)為“1”,說(shuō)明對(duì)象的偏向鎖生效了沉颂,同時(shí)也可以看到条摸,哪個(gè)線(xiàn)程獲得了該對(duì)象的鎖。

偏向鎖是jdk1.6引入的一項(xiàng)鎖優(yōu)化铸屉,其中的“偏”是偏心的偏钉蒲。它的意思就是說(shuō),這個(gè)鎖會(huì)偏向于第一個(gè)獲得它的線(xiàn)程彻坛,在接下來(lái)的執(zhí)行過(guò)程中顷啼,假如該鎖沒(méi)有被其他線(xiàn)程所獲取,沒(méi)有其他線(xiàn)程來(lái)競(jìng)爭(zhēng)該鎖昌屉,那么持有偏向鎖的線(xiàn)程將永遠(yuǎn)不需要進(jìn)行同步操作钙蒙。也就是說(shuō):在此線(xiàn)程之后的執(zhí)行過(guò)程中,如果再次進(jìn)入或者退出同一段同步塊代碼间驮,并不再需要去進(jìn)行加鎖或者解鎖操作躬厌,而是會(huì)做以下的步驟:

  1. Load-and-test,也就是簡(jiǎn)單判斷一下當(dāng)前線(xiàn)程id是否與Markword當(dāng)中的線(xiàn)程id是否一致.

  2. 如果一致竞帽,則說(shuō)明此線(xiàn)程已經(jīng)成功獲得了鎖扛施,繼續(xù)執(zhí)行下面的代碼.

  3. 如果不一致,則要檢查一下對(duì)象是否還是可偏向屹篓,即“是否偏向鎖”標(biāo)志位的值疙渣。

  4. 如果還未偏向抱虐,則利用CAS操作來(lái)競(jìng)爭(zhēng)鎖,也即是第一次獲取鎖時(shí)的操作饥脑。


釋放鎖

偏向鎖的釋放采用了一種只有競(jìng)爭(zhēng)才會(huì)釋放鎖的機(jī)制,線(xiàn)程是不會(huì)主動(dòng)去釋放偏向鎖刷钢,需要等待其他線(xiàn)程來(lái)競(jìng)爭(zhēng)内地。偏向鎖的撤銷(xiāo)需要等待全局安全點(diǎn)(這個(gè)時(shí)間點(diǎn)是上沒(méi)有正在執(zhí)行的代碼)。其步驟如下:

  1. 暫停擁有偏向鎖的線(xiàn)程荆针,判斷鎖對(duì)象石是否還處于被鎖定狀態(tài);

  2. 撤銷(xiāo)偏向鎖颁糟,恢復(fù)到無(wú)鎖狀態(tài)或者輕量級(jí)鎖的狀態(tài)航背;

安全點(diǎn)會(huì)導(dǎo)致stw(stop the word),導(dǎo)致性能下降棱貌,這種情況下應(yīng)當(dāng)禁用玖媚;

查看停頓–安全點(diǎn)停頓日志

要查看安全點(diǎn)停頓,可以打開(kāi)安全點(diǎn)日志婚脱,通過(guò)設(shè)置JVM參數(shù) -

XX:+PrintGCApplicationStoppedTime 會(huì)打出系統(tǒng)停止的時(shí)間今魔,

添加-XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1 這兩個(gè)參數(shù)會(huì)打印出詳細(xì)信息,可以查看到使用偏向鎖導(dǎo)致的停頓起惕,時(shí)間非常短暫涡贱,但是爭(zhēng)用嚴(yán)重的情況下,停頓次數(shù)也會(huì)非常多惹想;

注意:安全點(diǎn)日志不能一直打開(kāi):

  1. 安全點(diǎn)日志默認(rèn)輸出到stdout问词,一是stdout日志的整潔性,二是stdout所重定向的文件如果不在/dev/shm嘀粱,可能被鎖激挪。
  2. 對(duì)于一些很短的停頓,比如取消偏向鎖锋叨,打印的消耗比停頓本身還大垄分。
  3. 安全點(diǎn)日志是在安全點(diǎn)內(nèi)打印的,本身加大了安全點(diǎn)的停頓時(shí)間娃磺。

所以安全日志應(yīng)該只在問(wèn)題排查時(shí)打開(kāi)薄湿。
如果在生產(chǎn)系統(tǒng)上要打開(kāi),再再增加下面四個(gè)參數(shù):
-XX:+UnlockDiagnosticVMOptions -XX: -DisplayVMOutput -XX:+LogVMOutput -XX:LogFile=/dev/shm/vm.log
打開(kāi)Diagnostic(只是開(kāi)放了更多的flag可選,不會(huì)主動(dòng)激活某個(gè)flag)豺瘤,關(guān)掉輸出VM日志到stdout吆倦,輸出到獨(dú)立文件,/dev/shm目錄(內(nèi)存文件系統(tǒng))。

此日志分三部分:
第一部分是時(shí)間戳坐求,VM Operation的類(lèi)型

第二部分是線(xiàn)程概況蚕泽,被中括號(hào)括起來(lái)

total: 安全點(diǎn)里的總線(xiàn)程數(shù)
initially_running: 安全點(diǎn)時(shí)開(kāi)始時(shí)正在運(yùn)行狀態(tài)的線(xiàn)程數(shù)
wait_to_block: 在VM Operation開(kāi)始前需要等待其暫停的線(xiàn)程數(shù)

第三部分是到達(dá)安全點(diǎn)時(shí)的各個(gè)階段以及執(zhí)行操作所花的時(shí)間,其中最重要的是vmop

spin: 等待線(xiàn)程響應(yīng)safepoint號(hào)召的時(shí)間桥嗤;
block: 暫停所有線(xiàn)程所用的時(shí)間须妻;
sync: 等于 spin+block,這是從開(kāi)始到進(jìn)入安全點(diǎn)所耗的時(shí)間泛领,可用于判斷進(jìn)入安全點(diǎn)耗時(shí)荒吏;
cleanup: 清理所用時(shí)間;
vmop: 真正執(zhí)行VM Operation的時(shí)間师逸。
可見(jiàn)司倚,那些很多但又很短的安全點(diǎn),全都是RevokeBias篓像, 高并發(fā)的應(yīng)用會(huì)禁用掉偏向鎖动知。

jvm開(kāi)啟/關(guān)閉偏向鎖

開(kāi)啟偏向鎖:-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0
關(guān)閉偏向鎖:-XX:-UseBiasedLocking


輕量級(jí)鎖

自旋鎖的目標(biāo)是降低線(xiàn)程切換的成本。如果鎖競(jìng)爭(zhēng)激烈员辩,我們不得不依賴(lài)于重量級(jí)鎖盒粮,讓競(jìng)爭(zhēng)失敗的線(xiàn)程阻塞;如果完全沒(méi)有實(shí)際的鎖競(jìng)爭(zhēng)奠滑,那么申請(qǐng)重量級(jí)鎖都是浪費(fèi)的丹皱。輕量級(jí)鎖的目標(biāo)是,減少無(wú)實(shí)際競(jìng)爭(zhēng)情況下宋税,使用重量級(jí)鎖產(chǎn)生的性能消耗摊崭,包括系統(tǒng)調(diào)用引起的內(nèi)核態(tài)與用戶(hù)態(tài)切換、線(xiàn)程阻塞造成的線(xiàn)程切換等杰赛。

顧名思義呢簸,輕量級(jí)鎖是相對(duì)于重量級(jí)鎖而言的。使用輕量級(jí)鎖時(shí)乏屯,不需要申請(qǐng)互斥量根时,僅僅將Mark Word中的部分字節(jié)CAS更新指向線(xiàn)程棧中的Lock Record(Lock Record:JVM檢測(cè)到當(dāng)前對(duì)象是無(wú)鎖狀態(tài),則會(huì)在當(dāng)前線(xiàn)程的棧幀中創(chuàng)建一個(gè)名為L(zhǎng)OCKRECOD表空間用于copy Mark word 中的數(shù)據(jù))辰晕,如果更新成功蛤迎,則輕量級(jí)鎖獲取成功,記錄鎖狀態(tài)為輕量級(jí)鎖含友;否則替裆,說(shuō)明已經(jīng)有線(xiàn)程獲得了輕量級(jí)鎖校辩,目前發(fā)生了鎖競(jìng)爭(zhēng)(不適合繼續(xù)使用輕量級(jí)鎖),接下來(lái)膨脹為重量級(jí)鎖辆童。

當(dāng)然召川,由于輕量級(jí)鎖天然瞄準(zhǔn)不存在鎖競(jìng)爭(zhēng)的場(chǎng)景,如果存在鎖競(jìng)爭(zhēng)但不激烈胸遇,仍然可以用自旋鎖優(yōu)化,自旋失敗后再膨脹為重量級(jí)鎖汉形。

缺點(diǎn):同自旋鎖相似:如果鎖競(jìng)爭(zhēng)激烈纸镊,那么輕量級(jí)將很快膨脹為重量級(jí)鎖,那么維持輕量級(jí)鎖的過(guò)程就成了浪費(fèi)概疆。


重量級(jí)鎖

輕量級(jí)鎖膨脹之后逗威,就升級(jí)為重量級(jí)鎖了。重量級(jí)鎖是依賴(lài)對(duì)象內(nèi)部的monitor鎖來(lái)實(shí)現(xiàn)的岔冀,而monitor又依賴(lài)操作系統(tǒng)的MutexLock(互斥鎖)來(lái)實(shí)現(xiàn)的凯旭,所以重量級(jí)鎖也被成為互斥鎖。
當(dāng)輕量級(jí)所經(jīng)過(guò)鎖撤銷(xiāo)等步驟升級(jí)為重量級(jí)鎖之后使套,它的Markword部分?jǐn)?shù)據(jù)大體如下

為什么說(shuō)重量級(jí)鎖開(kāi)銷(xiāo)大呢

主要是罐呼,當(dāng)系統(tǒng)檢查到鎖是重量級(jí)鎖之后,會(huì)把等待想要獲得鎖的線(xiàn)程進(jìn)行阻塞侦高,被阻塞的線(xiàn)程不會(huì)消耗cup嫉柴。但是阻塞或者喚醒一個(gè)線(xiàn)程時(shí),都需要操作系統(tǒng)來(lái)幫忙奉呛,這就需要從用戶(hù)態(tài)轉(zhuǎn)換到內(nèi)核態(tài)计螺,而轉(zhuǎn)換狀態(tài)是需要消耗很多時(shí)間的,有可能比用戶(hù)執(zhí)行代碼的時(shí)間還要長(zhǎng)瞧壮。
這就是說(shuō)為什么重量級(jí)線(xiàn)程開(kāi)銷(xiāo)很大的登馒。

互斥鎖(重量級(jí)鎖)也稱(chēng)為阻塞同步、悲觀鎖


總結(jié)

偏向所鎖咆槽,輕量級(jí)鎖都是樂(lè)觀鎖陈轿,重量級(jí)鎖是悲觀鎖。

一個(gè)對(duì)象剛開(kāi)始實(shí)例化的時(shí)候罗晕,沒(méi)有任何線(xiàn)程來(lái)訪(fǎng)問(wèn)它的時(shí)候济欢。它是可偏向的,意味著小渊,它現(xiàn)在認(rèn)為只可能有一個(gè)線(xiàn)程來(lái)訪(fǎng)問(wèn)它法褥,所以當(dāng)?shù)谝粋€(gè)
線(xiàn)程來(lái)訪(fǎng)問(wèn)它的時(shí)候,它會(huì)偏向這個(gè)線(xiàn)程酬屉,此時(shí)半等,對(duì)象持有偏向鎖揍愁。偏向第一個(gè)線(xiàn)程,這個(gè)線(xiàn)程在修改對(duì)象頭成為偏向鎖的時(shí)候使用CAS操作杀饵,并將
對(duì)象頭中的ThreadID改成自己的ID莽囤,之后再次訪(fǎng)問(wèn)這個(gè)對(duì)象時(shí),只需要對(duì)比ID切距,不需要再使用CAS在進(jìn)行操作朽缎。

一旦有第二個(gè)線(xiàn)程訪(fǎng)問(wèn)這個(gè)對(duì)象,因?yàn)槠蜴i不會(huì)主動(dòng)釋放谜悟,所以第二個(gè)線(xiàn)程可以看到對(duì)象時(shí)偏向狀態(tài)话肖,這時(shí)表明在這個(gè)對(duì)象上已經(jīng)存在競(jìng)爭(zhēng)了,檢查原來(lái)持有該對(duì)象鎖的線(xiàn)程是否依然存活葡幸,如果掛了最筒,則可以將對(duì)象變?yōu)闊o(wú)鎖狀態(tài),然后重新偏向新的線(xiàn)程蔚叨,如果原來(lái)的線(xiàn)程依然存活床蜘,則馬上執(zhí)行那個(gè)線(xiàn)程的操作棧,檢查該對(duì)象的使用情況蔑水,如果仍然需要持有偏向鎖邢锯,則偏向鎖升級(jí)為輕量級(jí)鎖,(偏向鎖就是這個(gè)時(shí)候升級(jí)為輕量級(jí)鎖的)搀别。如果不存在使用了弹囚,則可以將對(duì)象回復(fù)成無(wú)鎖狀態(tài),然后重新偏向领曼。

輕量級(jí)鎖認(rèn)為競(jìng)爭(zhēng)存在鸥鹉,但是競(jìng)爭(zhēng)的程度很輕,一般兩個(gè)線(xiàn)程對(duì)于同一個(gè)鎖的操作都會(huì)錯(cuò)開(kāi)庶骄,或者說(shuō)稍微等待一下(自旋)毁渗,另一個(gè)線(xiàn)程就會(huì)釋放鎖。但是當(dāng)自旋超過(guò)一定的次數(shù)单刁,或者一個(gè)線(xiàn)程在持有鎖灸异,一個(gè)在自旋,又有第三個(gè)來(lái)訪(fǎng)時(shí)羔飞,輕量級(jí)鎖膨脹為重量級(jí)鎖肺樟,重量級(jí)鎖使除了擁有鎖的線(xiàn)程以外的線(xiàn)程都阻塞,防止CPU空轉(zhuǎn)逻淌。


引用(本文章只供本人學(xué)習(xí)以及學(xué)習(xí)的記錄么伯,如有侵權(quán),請(qǐng)聯(lián)系我刪除)

synchronized實(shí)現(xiàn)原理及鎖升級(jí)過(guò)程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末卡儒,一起剝皮案震驚了整個(gè)濱河市田柔,隨后出現(xiàn)的幾起案子俐巴,更是在濱河造成了極大的恐慌,老刑警劉巖硬爆,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件欣舵,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡缀磕,警方通過(guò)查閱死者的電腦和手機(jī)缘圈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)袜蚕,“玉大人准验,你說(shuō)我怎么就攤上這事⊥⒚唬” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵垂寥,是天一觀的道長(zhǎng)颠黎。 經(jīng)常有香客問(wèn)我,道長(zhǎng)滞项,這世上最難降的妖魔是什么狭归? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮文判,結(jié)果婚禮上过椎,老公的妹妹穿的比我還像新娘。我一直安慰自己戏仓,他們只是感情好疚宇,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著赏殃,像睡著了一般敷待。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仁热,一...
    開(kāi)封第一講書(shū)人閱讀 51,578評(píng)論 1 305
  • 那天榜揖,我揣著相機(jī)與錄音,去河邊找鬼抗蠢。 笑死举哟,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的迅矛。 我是一名探鬼主播妨猩,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼秽褒!你這毒婦竟也來(lái)了册赛?” 一聲冷哼從身側(cè)響起钠导,我...
    開(kāi)封第一講書(shū)人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎森瘪,沒(méi)想到半個(gè)月后牡属,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扼睬,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年逮栅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窗宇。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡措伐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出军俊,到底是詐尸還是另有隱情侥加,我是刑警寧澤,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布粪躬,位于F島的核電站担败,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏镰官。R本人自食惡果不足惜提前,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望泳唠。 院中可真熱鬧狈网,春花似錦、人聲如沸笨腥。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)脖母。三九已至拓售,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間镶奉,已是汗流浹背础淤。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哨苛,地道東北人鸽凶。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像建峭,于是被迫代替她去往敵國(guó)和親玻侥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容