關(guān)于 鎖的四種狀態(tài)與鎖升級過程 圖文詳解

一她倘、前言

鎖的狀態(tài)總共有四種璧微,級別由低到高依次為:無鎖、偏向鎖硬梁、輕量級鎖前硫、重量級鎖,這四種鎖狀態(tài)分別代表什么荧止,為什么會有鎖升級屹电?其實在 JDK 1.6之前,synchronized 還是一個重量級鎖跃巡,是一個效率比較低下的鎖枣察,但是在JDK 1.6后歌殃,Jvm為了提高鎖的獲取與釋放效率對(synchronized )進行了優(yōu)化援所,引入了 偏向鎖 和 輕量級鎖 不狮,從此以后鎖的狀態(tài)就有了四種(無鎖、偏向鎖兔朦、輕量級鎖偷线、重量級鎖),并且四種狀態(tài)會隨著競爭的情況逐漸升級沽甥,而且是不可逆的過程声邦,即不可降級,也就是說只能進行鎖升級(從低級別到高級別)摆舟,不能鎖降級(高級別到低級別)翔忽,意味著偏向鎖升級成輕量級鎖后不能降級成偏向鎖英融。這種鎖升級卻不能降級的策略,目的是為了提高獲得鎖和釋放鎖的效率歇式。

二、鎖的四種狀態(tài)

synchronized 最初的實現(xiàn)方式是 “阻塞或喚醒一個Java線程需要操作系統(tǒng)切換CPU狀態(tài)來完成胡野,這種狀態(tài)切換需要耗費處理器時間材失,如果同步代碼塊中內(nèi)容過于簡單,這種切換的時間可能比用戶代碼執(zhí)行的時間還長”硫豆,這種方式就是 synchronized實現(xiàn)同步最初的方式龙巨,這也是當初開發(fā)者詬病的地方,這也是在JDK6以前 synchronized效率低下的原因熊响,JDK6中為了減少獲得鎖和釋放鎖帶來的性能消耗旨别,引入了“偏向鎖”和“輕量級鎖”。

所以目前鎖狀態(tài)一種有四種汗茄,從級別由低到高依次是:無鎖秸弛、偏向鎖,輕量級鎖洪碳,重量級鎖递览,鎖狀態(tài)只能升級,不能降級

如圖所示:

在這里插入圖片描述

三瞳腌、鎖狀態(tài)的思路以及特點

鎖狀態(tài) 存儲內(nèi)容 標志位
無鎖 對象的hashCode绞铃、對象分代年齡、是否是偏向鎖(0) 01
偏向鎖 偏向線程ID嫂侍、偏向時間戳儿捧、對象分代年齡、是否是偏向鎖(1) 01
輕量級鎖 指向棧中鎖記錄的指針 00
重量級鎖 指向互斥量的指針 11

四挑宠、鎖對比

優(yōu)點 缺點 適用場景
偏向鎖 加鎖和解鎖不需要額外的消耗菲盾,和執(zhí)行非同步方法相比僅存在納秒級的差距 如果線程間存在鎖競爭,會帶來額外的鎖撤銷的消耗 適用于只有一個線程訪問同步塊場景
輕量級鎖 競爭的線程不會阻塞痹栖,提高了程序的響應速度 如果始終得不到索競爭的線程亿汞,使用自旋會消耗CPU 追求響應速度,同步塊執(zhí)行速度非尘景ⅲ快
重量級鎖 線程競爭不使用自旋疗我,不會消耗CPU 線程阻塞,響應時間緩慢 追求吞吐量南捂,同步塊執(zhí)行速度較慢

五吴裤、Synchronized鎖

synchronized 用的鎖是存在Java對象頭里的,那么什么是對象頭呢溺健?

5.1 Java 對象頭

我們以 Hotspot 虛擬機為例麦牺,Hopspot 對象頭主要包括兩部分數(shù)據(jù):Mark Word(標記字段) 和 Klass Pointer(類型指針)

Mark Word:默認存儲對象的HashCode,分代年齡和鎖標志位信息。這些信息都是與對象自身定義無關(guān)的數(shù)據(jù)剖膳,所以Mark Word被設(shè)計成一個非固定的數(shù)據(jù)結(jié)構(gòu)以便在極小的空間內(nèi)存存儲盡量多的數(shù)據(jù)魏颓。它會根據(jù)對象的狀態(tài)復用自己的存儲空間,也就是說在運行期間Mark Word里存儲的數(shù)據(jù)會隨著鎖標志位的變化而變化吱晒。

Klass Point:對象指向它的類元數(shù)據(jù)的指針甸饱,虛擬機通過這個指針來確定這個對象是哪個類的實例。

在上面中我們知道了仑濒,synchronized 用的鎖是存在Java對象頭里的叹话,那么具體是存在對象頭哪里呢?答案是:存在鎖對象的對象頭的Mark Word中墩瞳,那么MarkWord在對象頭中到底長什么樣驼壶,它到底存儲了什么呢?

在64位的虛擬機中:

在這里插入圖片描述

在32位的虛擬機中:
在這里插入圖片描述

下面我們以 32位虛擬機為例喉酌,來看一下其 Mark Word 的字節(jié)具體是如何分配的

無鎖:對象頭開辟 25bit 的空間用來存儲對象的 hashcode 热凹,4bit 用于存放對象分代年齡,1bit 用來存放是否偏向鎖的標識位瞭吃,2bit 用來存放鎖標識位為01

偏向鎖: 在偏向鎖中劃分更細碌嘀,還是開辟 25bit 的空間,其中23bit 用來存放線程ID歪架,2bit 用來存放 Epoch股冗,4bit 存放對象分代年齡,1bit 存放是否偏向鎖標識和蚪, 0表示無鎖止状,1表示偏向鎖,鎖的標識位還是01

輕量級鎖:在輕量級鎖中直接開辟 30bit 的空間存放指向棧中鎖記錄的指針攒霹,2bit 存放鎖的標志位怯疤,其標志位為00

重量級鎖: 在重量級鎖中和輕量級鎖一樣,30bit 的空間用來存放指向重量級鎖的指針催束,2bit 存放鎖的標識位集峦,為11

GC標記: 開辟30bit 的內(nèi)存空間卻沒有占用,2bit 空間存放鎖標志位為11抠刺。

其中無鎖和偏向鎖的鎖標志位都是01塔淤,只是在前面的1bit區(qū)分了這是無鎖狀態(tài)還是偏向鎖狀態(tài)

關(guān)于內(nèi)存的分配,我們可以在git中openJDK中 markOop.hpp 可以看出:

public:
  // Constants
  enum { age_bits                 = 4,
         lock_bits                = 2,
         biased_lock_bits         = 1,
         max_hash_bits            = BitsPerWord - age_bits - lock_bits - biased_lock_bits,
         hash_bits                = max_hash_bits > 31 ? 31 : max_hash_bits,
         cms_bits                 = LP64_ONLY(1) NOT_LP64(0),
         epoch_bits               = 2
  };
  • age_bits: 就是我們說的分代回收的標識速妖,占用4字節(jié)
  • lock_bits: 是鎖的標志位高蜂,占用2個字節(jié)
  • biased_lock_bits: 是是否偏向鎖的標識,占用1個字節(jié)
  • max_hash_bits: 是針對無鎖計算的hashcode 占用字節(jié)數(shù)量罕容,如果是32位虛擬機备恤,就是 32 - 4 - 2 -1 = 25 byte稿饰,如果是64 位虛擬機,64 - 4 - 2 - 1 = 57 byte露泊,但是會有 25 字節(jié)未使用喉镰,所以64位的 hashcode 占用 31 byte
  • hash_bits: 是針對 64 位虛擬機來說,如果最大字節(jié)數(shù)大于 31滤淳,則取31梧喷,否則取真實的字節(jié)數(shù)
  • cms_bits: 不是64位虛擬機就占用 0 byte,是64位就占用 1byte
  • epoch_bits: 就是 epoch 所占用的字節(jié)大小脖咐,2字節(jié)。

5.2 Monitor

Monitor 可以理解為一個同步工具或一種同步機制汇歹,通常被描述為一個對象屁擅。每一個 Java 對象就有一把看不見的鎖,稱為內(nèi)部鎖或者 Monitor 鎖产弹。

Monitor 是線程私有的數(shù)據(jù)結(jié)構(gòu)派歌,每一個線程都有一個可用 monitor record 列表,同時還有一個全局的可用列表痰哨。每一個被鎖住的對象都會和一個 monitor 關(guān)聯(lián)胶果,同時 monitor 中有一個 Owner 字段存放擁有該鎖的線程的唯一標識,表示該鎖被這個線程占用斤斧。

Synchronized是通過對象內(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)的鎖我們稱之為重量級鎖。

隨著鎖的競爭烘豌,鎖可以從偏向鎖升級到輕量級鎖载庭,再升級的重量級鎖(但是鎖的升級是單向的,也就是說只能從低到高升級廊佩,不會出現(xiàn)鎖的降級)囚聚。JDK 1.6中默認是開啟偏向鎖和輕量級鎖的,我們也可以通過-XX:-UseBiasedLocking=false來禁用偏向鎖罐寨。

六靡挥、鎖的分類

6.2 無鎖

無鎖是指沒有對資源進行鎖定,所有的線程都能訪問并修改同一個資源鸯绿,但同時只有一個線程能修改成功跋破。

無鎖的特點是修改操作會在循環(huán)內(nèi)進行簸淀,線程會不斷的嘗試修改共享資源。如果沒有沖突就修改成功并退出毒返,否則就會繼續(xù)循環(huán)嘗試租幕。如果有多個線程修改同一個值,必定會有一個線程能修改成功拧簸,而其他修改失敗的線程會不斷重試直到修改成功劲绪。

6.3 偏向鎖

初次執(zhí)行到synchronized代碼塊的時候,鎖對象變成偏向鎖(通過CAS修改對象頭里的鎖標志位)盆赤,字面意思是“偏向于第一個獲得它的線程”的鎖贾富。執(zhí)行完同步代碼塊后,線程并不會主動釋放偏向鎖牺六。當?shù)诙蔚竭_同步代碼塊時颤枪,線程會判斷此時持有鎖的線程是否就是自己(持有鎖的線程ID也在對象頭里),如果是則正常往下執(zhí)行淑际。由于之前沒有釋放鎖畏纲,這里也就不需要重新加鎖。如果自始至終使用鎖的線程只有一個春缕,很明顯偏向鎖幾乎沒有額外開銷盗胀,性能極高。

偏向鎖是指當一段同步代碼一直被同一個線程所訪問時锄贼,即不存在多個線程的競爭時票灰,那么該線程在后續(xù)訪問時便會自動獲得鎖,從而降低獲取鎖帶來的消耗咱娶,即提高性能米间。

當一個線程訪問同步代碼塊并獲取鎖時,會在 Mark Word 里存儲鎖偏向的線程 ID膘侮。在線程進入和退出同步塊時不再通過 CAS 操作來加鎖和解鎖屈糊,而是檢測 Mark Word 里是否存儲著指向當前線程的偏向鎖。輕量級鎖的獲取及釋放依賴多次 CAS 原子指令琼了,而偏向鎖只需要在置換 ThreadID 的時候依賴一次 CAS 原子指令即可逻锐。

偏向鎖只有遇到其他線程嘗試競爭偏向鎖時,持有偏向鎖的線程才會釋放鎖雕薪,線程是不會主動釋放偏向鎖的昧诱。

關(guān)于偏向鎖的撤銷,需要等待全局安全點所袁,即在某個時間點上沒有字節(jié)碼正在執(zhí)行時盏档,它會先暫停擁有偏向鎖的線程,然后判斷鎖對象是否處于被鎖定狀態(tài)燥爷。如果線程不處于活動狀態(tài)蜈亩,則將對象頭設(shè)置成無鎖狀態(tài)懦窘,并撤銷偏向鎖,恢復到無鎖(標志位為01)或輕量級鎖(標志位為00)的狀態(tài)稚配。

6.4 輕量級鎖(自旋鎖)

在這里插入圖片描述

輕量級鎖是指當鎖是偏向鎖的時候畅涂,卻被另外的線程所訪問,此時偏向鎖就會升級為輕量級鎖道川,其他線程會通過自旋(關(guān)于自旋的介紹見文末)的形式嘗試獲取鎖午衰,線程不會阻塞,從而提高性能冒萄。

輕量級鎖的獲取主要由兩種情況:
① 當關(guān)閉偏向鎖功能時臊岸;
② 由于多個線程競爭偏向鎖導致偏向鎖升級為輕量級鎖。

一旦有第二個線程加入鎖競爭尊流,偏向鎖就升級為輕量級鎖(自旋鎖)扇单。這里要明確一下什么是鎖競爭:如果多個線程輪流獲取一個鎖,但是每次獲取鎖的時候都很順利奠旺,沒有發(fā)生阻塞,那么就不存在鎖競爭施流。只有當某線程嘗試獲取鎖的時候响疚,發(fā)現(xiàn)該鎖已經(jīng)被占用,只能等待其釋放瞪醋,這才發(fā)生了鎖競爭忿晕。

在輕量級鎖狀態(tài)下繼續(xù)鎖競爭,沒有搶到鎖的線程將自旋银受,即不停地循環(huán)判斷鎖是否能夠被成功獲取践盼。獲取鎖的操作,其實就是通過CAS修改對象頭里的鎖標志位宾巍。先比較當前鎖標志位是否為“釋放”咕幻,如果是則將其設(shè)置為“鎖定”,比較并設(shè)置是原子性發(fā)生的顶霞。這就算搶到鎖了肄程,然后線程將當前鎖的持有者信息修改為自己。

長時間的自旋操作是非常消耗資源的选浑,一個線程持有鎖蓝厌,其他線程就只能在原地空耗CPU,執(zhí)行不了任何有效的任務古徒,這種現(xiàn)象叫做忙等(busy-waiting)拓提。如果多個線程用一個鎖,但是沒有發(fā)生鎖競爭隧膘,或者發(fā)生了很輕微的鎖競爭代态,那么synchronized就用輕量級鎖寺惫,允許短時間的忙等現(xiàn)象。這是一種折衷的想法胆数,短時間的忙等肌蜻,換取線程在用戶態(tài)和內(nèi)核態(tài)之間切換的開銷。

6.4 重量級鎖

重量級鎖顯然必尼,此忙等是有限度的(有個計數(shù)器記錄自旋次數(shù)蒋搜,默認允許循環(huán)10次,可以通過虛擬機參數(shù)更改)判莉。如果鎖競爭情況嚴重豆挽,某個達到最大自旋次數(shù)的線程,會將輕量級鎖升級為重量級鎖(依然是CAS修改鎖標志位券盅,但不修改持有鎖的線程ID)帮哈。當后續(xù)線程嘗試獲取鎖時,發(fā)現(xiàn)被占用的鎖是重量級鎖锰镀,則直接將自己掛起(而不是忙等)娘侍,等待將來被喚醒。

重量級鎖是指當有一個線程獲取鎖之后泳炉,其余所有等待獲取該鎖的線程都會處于阻塞狀態(tài)憾筏。

簡言之,就是所有的控制權(quán)都交給了操作系統(tǒng)花鹅,由操作系統(tǒng)來負責線程間的調(diào)度和線程的狀態(tài)變更氧腰。而這樣會出現(xiàn)頻繁地對線程運行狀態(tài)的切換,線程的掛起和喚醒刨肃,從而消耗大量的系統(tǒng)資

五古拴、總結(jié)

文中講述了鎖的四種狀態(tài)以及鎖是如何一步一步升級的過程,文中有理解不到位或者有問題的地方真友,歡迎大家在評論區(qū)中下方指出和交流黄痪,謝謝大家

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市锻狗,隨后出現(xiàn)的幾起案子满力,更是在濱河造成了極大的恐慌,老刑警劉巖轻纪,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件油额,死亡現(xiàn)場離奇詭異,居然都是意外死亡刻帚,警方通過查閱死者的電腦和手機潦嘶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來崇众,“玉大人掂僵,你說我怎么就攤上這事航厚。” “怎么了锰蓬?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵幔睬,是天一觀的道長。 經(jīng)常有香客問我芹扭,道長麻顶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任舱卡,我火速辦了婚禮辅肾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘轮锥。我一直安慰自己矫钓,他們只是感情好,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布舍杜。 她就那樣靜靜地躺著新娜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪既绩。 梳的紋絲不亂的頭發(fā)上杯活,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音熬词,去河邊找鬼。 笑死吸重,一個胖子當著我的面吹牛互拾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嚎幸,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼颜矿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了嫉晶?” 一聲冷哼從身側(cè)響起骑疆,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎替废,沒想到半個月后箍铭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡椎镣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年诈火,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片状答。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡冷守,死狀恐怖刀崖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拍摇,我是刑警寧澤亮钦,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站充活,受9級特大地震影響蜂莉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜堪唐,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一巡语、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧淮菠,春花似錦男公、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拥知,卻和暖如春踏拜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背低剔。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工速梗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人襟齿。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓姻锁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親猜欺。 傳聞我的和親對象是個殘疾皇子位隶,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345