多線程高并發(fā):synchronized鎖升級(jí)過程及其實(shí)現(xiàn)原理

問:為什么會(huì)有鎖升級(jí)的過程呢聪蘸?

答:在java6以前synchronized鎖實(shí)現(xiàn)都是重量級(jí)鎖的形式扎附,效率低下第煮,為了提升效率進(jìn)行了優(yōu)化,所以出現(xiàn)了鎖升級(jí)的過程放仗。

問:我們通常說synchronized鎖是重量級(jí)鎖润绎,那么為什么叫他重量級(jí)鎖?

答:因?yàn)閟ynchronized執(zhí)行效率太低匙监。在java1.6以前每次調(diào)用synchronized加鎖時(shí)都需要進(jìn)行系統(tǒng)調(diào)用凡橱,系統(tǒng)調(diào)用會(huì)涉及到用戶態(tài)和內(nèi)核態(tài)的切換,系統(tǒng)調(diào)用會(huì)經(jīng)過0x80中斷亭姥,經(jīng)過內(nèi)核調(diào)用后再返回用戶態(tài)。此過程比較復(fù)雜時(shí)間比較長所以通常叫synchronized為重量級(jí)鎖顾稀。

誤區(qū):其實(shí)鎖升級(jí)過程中涉及到的鎖偏向鎖达罗,輕量級(jí)鎖都是synchronized鎖的具體實(shí)現(xiàn)所要經(jīng)歷的過程,他們并不是單獨(dú)的鎖静秆。只是給他們這幾種鎖的狀態(tài)起了一個(gè)名字而已粮揉。

CAS

在介紹synchronized鎖升級(jí)過程之前,我們需要先了解cas的原理抚笔,為什么呢扶认?因?yàn)閏as貫穿了整個(gè)synchronized鎖升級(jí)的過程。

CAS : compare and swap 或者 compare and exchange 比較交換殊橙。

當(dāng)我們需要對(duì)內(nèi)存中的數(shù)據(jù)進(jìn)行修改操作時(shí)辐宾,為了避免多線程并發(fā)修改的情況,我們在對(duì)它進(jìn)行修改操作前膨蛮,先讀取它原來的值E叠纹,然后進(jìn)行計(jì)算得出新的的值V,在修改前去比較當(dāng)前內(nèi)存中的值N是否和我之前讀到的E相同敞葛,如果相同誉察,認(rèn)為其他線程沒有修改過內(nèi)存中的值,如果不同惹谐,說明被其他線程修改了持偏,這時(shí)驼卖,要繼續(xù)循環(huán)去獲取最新的值E,再進(jìn)行計(jì)算和比較鸿秆,直到我們預(yù)期的值和當(dāng)前內(nèi)存中的值相等時(shí)款慨,再對(duì)數(shù)據(jù)執(zhí)行修改操作。

CAS具體流程如下下圖:

image.png

它是為了實(shí)現(xiàn)java中的原子操作而出現(xiàn)的谬莹。為了保證在比較完成后賦值這兩個(gè)操作的原子性檩奠,jvm內(nèi)部實(shí)現(xiàn)cas操作時(shí)通過LOCK CMPXCHG指令鎖cpu總線方式實(shí)現(xiàn)原子操作的。

對(duì)象頭

synchronized用的鎖是存在java對(duì)象頭里的附帽。

32位java對(duì)象頭結(jié)構(gòu)如下表所示:


image.png

對(duì)于64位的java對(duì)象頭其余信息基本不變埠戳,只是中間有關(guān)于對(duì)象hashcode值和之后加鎖信息的位數(shù)加大以外,其他基本不變蕉扮。
64位虛擬機(jī)系統(tǒng)下java對(duì)象頭在不同鎖狀態(tài)下的狀態(tài)變化如下表所示:

image.png

如上圖所示:其中最后兩位代表是否加鎖的標(biāo)志位整胃。鎖標(biāo)志位如果是01的話需要根據(jù)前一位的是否為偏向鎖來判斷當(dāng)前的鎖狀態(tài),如果前一位為0則代表無鎖狀態(tài)喳钟,如果為1則代表有偏向鎖屁使。

后兩位:00代表輕量級(jí)鎖,10代表重量級(jí)鎖奔则,11代表GC垃圾回收的標(biāo)記信息蛮寂。

偏向鎖

偏向鎖產(chǎn)生的原因?

大多數(shù)情況下易茬,鎖不僅不存在多線程競爭酬蹋,而且總是由同一線程多次獲得,為了讓線程獲得鎖的代價(jià)更低而引入了偏向鎖抽莱。

獲取偏向鎖流程:

當(dāng)一個(gè)線程訪問同步塊時(shí)范抓,會(huì)先判斷鎖標(biāo)志位是否為01,如果是01食铐,則判斷是否為偏向鎖匕垫,如果是,會(huì)先判斷當(dāng)前鎖對(duì)象頭中是否存儲(chǔ)了當(dāng)前的線程id虐呻,如果存儲(chǔ)了象泵,則直接獲得鎖。如果對(duì)象頭中指向不是當(dāng)前線程id铃慷,則通過CAS嘗試將自己的線程id存儲(chǔ)進(jìn)當(dāng)前鎖對(duì)象的對(duì)象頭中來獲取偏向鎖单芜。當(dāng)cas嘗試獲取偏向鎖成功后則繼續(xù)執(zhí)行同步代碼塊,否則等待安全點(diǎn)的到來撤銷原來線程的偏向鎖犁柜,撤銷時(shí)需要暫停原持有偏向鎖的線程洲鸠,判斷線程是否活動(dòng)狀態(tài),如果已經(jīng)退出同步代碼塊則喚醒新的線程開始獲取偏向鎖,否則開始鎖競爭進(jìn)行鎖升級(jí)過程扒腕,升級(jí)為輕量級(jí)鎖绢淀。

偏向鎖獲取流程如下圖:

image.png

在高并發(fā)下可以關(guān)閉偏向鎖來提升性能,通過設(shè)置JVM參數(shù) -XX:-UseBiasedLocking=false瘾腰。

輕量級(jí)鎖

當(dāng)出現(xiàn)鎖競爭時(shí)皆的,會(huì)升級(jí)為輕量級(jí)鎖。

在升級(jí)輕量級(jí)鎖之前蹋盆,JVM會(huì)先在當(dāng)前線程的棧幀中創(chuàng)建用于存儲(chǔ)鎖記錄的空間即將對(duì)象頭中用來標(biāo)記鎖信息相關(guān)的內(nèi)容封裝成一個(gè)java對(duì)象放入當(dāng)前線程的棧幀中费薄,這個(gè)對(duì)象稱為LockRcord,然后線程嘗試通過CAS將對(duì)象頭中mark word替換為指向鎖記錄(lockrecord)的指針栖雾。如果成功則當(dāng)前線程獲取鎖楞抡,如果失敗則使用自旋來獲取鎖。自旋其實(shí)就是不斷的循環(huán)進(jìn)行CAS操作直到能成功替換析藕。所以輕量級(jí)鎖又叫自旋鎖召廷。

下圖來源于網(wǎng)絡(luò)

棧上分配LockRecord: lockrecord中包含了對(duì)象的引用地址。

對(duì)象頭中markword替換鎖記錄指針成功之后如下圖:

image.png

替換成功之后將鎖標(biāo)志位改為00 表示獲取輕量級(jí)鎖成功账胧。

lockrecord的作用:在這里實(shí)現(xiàn)了鎖重入竞慢,每當(dāng)同一個(gè)線程多次獲取同一個(gè)鎖時(shí),會(huì)在當(dāng)前棧幀中放入一個(gè)lockrecord治泥,但是重入是放入的lockrecord關(guān)于鎖信息的內(nèi)容為null筹煮,代表鎖重入。當(dāng)輕量級(jí)解鎖時(shí)车摄,每解鎖一次則從棧幀中彈出一個(gè)lockrecord寺谤,直到為0.

輕量級(jí)鎖重入之后如下圖:

image.png

當(dāng)通過CAS自旋獲取輕量級(jí)鎖達(dá)到一定次數(shù)時(shí),JVM會(huì)發(fā)生鎖膨脹升級(jí)為重量級(jí)鎖吮播。

原因:不斷的自旋在高并發(fā)的下會(huì)消耗大量的cpu資源,所以jvm為了節(jié)省cpu資源眼俊,進(jìn)行了鎖升級(jí)意狠。將等待獲取鎖的線程都放入一個(gè)等待隊(duì)列中來節(jié)省cpu資源。

重量級(jí)鎖

在重量級(jí)鎖中將LockRecord對(duì)象替換為了monitor對(duì)象的實(shí)現(xiàn)疮胖。主要通過monitorenter和monitorexit兩個(gè)指令來實(shí)現(xiàn)环戈。需要經(jīng)過系統(tǒng)調(diào)用,在并發(fā)低的情況下效率會(huì)低澎灸。

通過openJDK可以查看ObjectMonitor對(duì)象的結(jié)構(gòu):
http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/9758d9f36299/src/share/vm/runtime/objectMonitor.hpp

ObjectMonitor() {
    _header       = NULL;
    _count        = 0;
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL; //擁有當(dāng)前對(duì)象的線程
    _WaitSet      = NULL; //阻塞隊(duì)列
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    _EntryList    = NULL ; //有資格成為候選資源的線程隊(duì)列
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0;
  }

使用monitor加鎖如下圖:

image.png

重量級(jí)鎖在進(jìn)行鎖重入的時(shí)候每獲取到鎖一次會(huì)對(duì)monitor對(duì)象中的計(jì)數(shù)器+1院塞,等鎖退出時(shí)則會(huì)相應(yīng)的-1,直到減到0為止性昭,鎖完全退出拦止。

幾種鎖狀態(tài)優(yōu)缺點(diǎn)對(duì)比

https://p26.toutiaoimg.com/origin/pgc-image/a983c3bbdf814797add6d3a7515dfe10?from=pc

總結(jié)

綜上,我們發(fā)現(xiàn)偏向鎖,輕量級(jí)鎖(又稱自旋鎖或無鎖)汹族,重量級(jí)鎖都是synchronized鎖鎖實(shí)現(xiàn)中鎖經(jīng)歷的幾種不同的狀態(tài)萧求。

三種鎖狀態(tài)的場景總結(jié):

  • 只有一個(gè)線程進(jìn)入臨界區(qū) -------偏向鎖
  • 多個(gè)線程交替進(jìn)入臨界區(qū)--------輕量級(jí)鎖
  • 多個(gè)線程同時(shí)進(jìn)入臨界區(qū)-------重量級(jí)鎖

作者:little_color

原文鏈接:
https://blog.csdn.net/wangyy130/article/details/106495180

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市顶瞒,隨后出現(xiàn)的幾起案子夸政,更是在濱河造成了極大的恐慌,老刑警劉巖榴徐,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件守问,死亡現(xiàn)場離奇詭異,居然都是意外死亡坑资,警方通過查閱死者的電腦和手機(jī)耗帕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來盐茎,“玉大人兴垦,你說我怎么就攤上這事∽帜” “怎么了探越?”我有些...
    開封第一講書人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長窑业。 經(jīng)常有香客問我钦幔,道長,這世上最難降的妖魔是什么常柄? 我笑而不...
    開封第一講書人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任鲤氢,我火速辦了婚禮,結(jié)果婚禮上西潘,老公的妹妹穿的比我還像新娘卷玉。我一直安慰自己,他們只是感情好喷市,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開白布相种。 她就那樣靜靜地躺著,像睡著了一般品姓。 火紅的嫁衣襯著肌膚如雪寝并。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評(píng)論 1 305
  • 那天腹备,我揣著相機(jī)與錄音衬潦,去河邊找鬼。 笑死植酥,一個(gè)胖子當(dāng)著我的面吹牛镀岛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼哎媚,長吁一口氣:“原來是場噩夢啊……” “哼喇伯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拨与,我...
    開封第一講書人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤稻据,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后买喧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捻悯,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年淤毛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了今缚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡低淡,死狀恐怖姓言,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蔗蹋,我是刑警寧澤何荚,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站猪杭,受9級(jí)特大地震影響餐塘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜皂吮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一戒傻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蜂筹,春花似錦需纳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至闺属,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間周霉,已是汗流浹背掂器。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留俱箱,地道東北人国瓮。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親乃摹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子禁漓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

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