淺談偏向鎖奶甘、輕量級鎖篷店、重量級鎖

為了換取性能,JVM在內置鎖上做了非常多的優(yōu)化,膨脹式的鎖分配策略就是其一疲陕。理解偏向鎖方淤、輕量級鎖、重量級鎖的要解決的基本問題蹄殃,幾種鎖的分配和膨脹過程携茂,有助于編寫并優(yōu)化基于鎖的并發(fā)程序。

內置鎖的分配和膨脹過程較為復雜诅岩,限于時間和精力讳苦,文中該部分內容是根據網上的多方資料整合而來;僅為方便查閱吩谦,后面繼續(xù)分析JVM源碼的時候也有個參考鸳谜。如果對各級鎖已經有了基本了解,讀者大可跳過此文式廷。

隱藏在內置鎖下的基本問題

內置鎖是JVM提供的最便捷的線程同步工具咐扭,在代碼塊或方法聲明上添加synchronized關鍵字即可使用內置鎖。使用內置鎖能夠簡化并發(fā)模型懒棉;隨著JVM的升級草描,幾乎不需要修改代碼览绿,就可以直接享受JVM在內置鎖上的優(yōu)化成果策严。從簡單的重量級鎖,到逐漸膨脹的鎖分配策略饿敲,使用了多種優(yōu)化手段解決隱藏在內置鎖下的基本問題妻导。

重量級鎖

內置鎖在Java中被抽象為監(jiān)視器鎖(monitor)。在JDK 1.6之前怀各,監(jiān)視器鎖可以認為直接對應底層操作系統(tǒng)中的互斥量(mutex)倔韭。這種同步方式的成本非常高,包括系統(tǒng)調用引起的內核態(tài)與用戶態(tài)切換瓢对、線程阻塞造成的線程切換等寿酌。因此,后來稱這種鎖為“重量級鎖”硕蛹。

自旋鎖

首先醇疼,內核態(tài)與用戶態(tài)的切換上不容易優(yōu)化。但通過自旋鎖法焰,可以減少線程阻塞造成的線程切換(包括掛起線程和恢復線程)秧荆。

如果鎖的粒度小,那么鎖的持有時間比較短(盡管具體的持有時間無法得知埃仪,但可以認為乙濒,通常有一部分鎖能滿足上述性質)。那么卵蛉,對于競爭這些鎖的而言颁股,因為鎖阻塞造成線程切換的時間與鎖持有的時間相當么库,減少線程阻塞造成的線程切換,能得到較大的性能提升甘有。具體如下:

  • 當前線程競爭鎖失敗時廊散,打算阻塞自己
  • 不直接阻塞自己,而是自旋(空等待梧疲,比如一個空的有限for循環(huán))一會
  • 在自旋的同時重新競爭鎖
  • 如果自旋結束前獲得了鎖允睹,那么鎖獲取成功;否則幌氮,自旋結束后阻塞自己

如果在自旋的時間內缭受,鎖就被舊owner釋放了,那么當前線程就不需要阻塞自己(也不需要在未來鎖釋放時恢復)该互,減少了一次線程切換米者。

“鎖的持有時間比較短”這一條件可以放寬。實際上宇智,只要鎖競爭的時間比較短(比如線程1快釋放鎖的時候蔓搞,線程2才會來競爭鎖),就能夠提高自旋獲得鎖的概率随橘。這通常發(fā)生在鎖持有時間長喂分,但競爭不激烈的場景中。

缺點

  • 單核處理器上机蔗,不存在實際的并行蒲祈,當前線程不阻塞自己的話,舊owner就不能執(zhí)行萝嘁,鎖永遠不會釋放梆掸,此時不管自旋多久都是浪費;進而牙言,如果線程多而處理器少酸钦,自旋也會造成不少無謂的浪費。
  • 自旋鎖要占用CPU咱枉,如果是計算密集型任務卑硫,這一優(yōu)化通常得不償失,減少鎖的使用是更好的選擇庞钢。
  • 如果鎖競爭的時間比較長拔恰,那么自旋通常不能獲得鎖,白白浪費了自旋占用的CPU時間基括。這通常發(fā)生在鎖持有時間長颜懊,且競爭激烈的場景中,此時應主動禁用自旋鎖。

使用-XX:-UseSpinning參數關閉自旋鎖優(yōu)化河爹;-XX:PreBlockSpin參數修改默認的自旋次數匠璧。

自適應自旋鎖

自適應意味著自旋的時間不再固定了,而是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態(tài)來決定:

  • 如果在同一個鎖對象上咸这,自旋等待剛剛成功獲得過鎖夷恍,并且持有鎖的線程正在運行中,那么虛擬機就會認為這次自旋也很有可能再次成功媳维,進而它將允許自旋等待持續(xù)相對更長的時間酿雪,比如100個循環(huán)。
  • 相反的侄刽,如果對于某個鎖指黎,自旋很少成功獲得過,那在以后要獲取這個鎖時將可能減少自旋時間甚至省略自旋過程州丹,以避免浪費處理器資源醋安。

自適應自旋解決的是“鎖競爭時間不確定”的問題。JVM很難感知到確切的鎖競爭時間墓毒,而交給用戶分析就違反了JVM的設計初衷吓揪。自適應自旋假定不同線程持有同一個鎖對象的時間基本相當,競爭程度趨于穩(wěn)定所计,因此柠辞,可以根據上一次自旋的時間與結果調整下一次自旋的時間

缺點

然而醉箕,自適應自旋也沒能徹底解決該問題钾腺,如果默認的自旋次數設置不合理(過高或過低),那么自適應的過程將很難收斂到合適的值讥裤。

輕量級鎖

自旋鎖的目標是降低線程切換的成本。如果鎖競爭激烈姻报,我們不得不依賴于重量級鎖己英,讓競爭失敗的線程阻塞;如果完全沒有實際的鎖競爭吴旋,那么申請重量級鎖都是浪費的损肛。輕量級鎖的目標是,減少無實際競爭情況下荣瑟,使用重量級鎖產生的性能消耗治拿,包括系統(tǒng)調用引起的內核態(tài)與用戶態(tài)切換、線程阻塞造成的線程切換等笆焰。

顧名思義劫谅,輕量級鎖是相對于重量級鎖而言的。使用輕量級鎖時,不需要申請互斥量捏检,僅僅將Mark Word中的部分字節(jié)CAS更新指向線程棧中的Lock Record荞驴,如果更新成功,則輕量級鎖獲取成功贯城,記錄鎖狀態(tài)為輕量級鎖熊楼;否則,說明已經有線程獲得了輕量級鎖能犯,目前發(fā)生了鎖競爭(不適合繼續(xù)使用輕量級鎖)鲫骗,接下來膨脹為重量級鎖

Mark Word是對象頭的一部分踩晶;每個線程都擁有自己的線程棧(虛擬機棧)挎峦,記錄線程和函數調用的基本信息。二者屬于JVM的基礎內容合瓢,此處不做介紹坦胶。

當然,由于輕量級鎖天然瞄準不存在鎖競爭的場景晴楔,如果存在鎖競爭但不激烈顿苇,仍然可以用自旋鎖優(yōu)化,自旋失敗后再膨脹為重量級鎖税弃。

缺點

同自旋鎖相似:

  • 如果鎖競爭激烈纪岁,那么輕量級將很快膨脹為重量級鎖,那么維持輕量級鎖的過程就成了浪費则果。

偏向鎖

在沒有實際競爭的情況下幔翰,還能夠針對部分場景繼續(xù)優(yōu)化。如果不僅僅沒有實際競爭西壮,自始至終遗增,使用鎖的線程都只有一個,那么款青,維護輕量級鎖都是浪費的做修。偏向鎖的目標是,減少無競爭且只有一個線程使用鎖的情況下抡草,使用輕量級鎖產生的性能消耗饰及。輕量級鎖每次申請、釋放鎖都至少需要一次CAS康震,但偏向鎖只有初始化時需要一次CAS燎含。

“偏向”的意思是,偏向鎖假定將來只有第一個申請鎖的線程會使用鎖(不會有任何線程再來申請鎖)腿短,因此屏箍,只需要在Mark Word中CAS記錄owner(本質上也是更新绘梦,但初始值為空),如果記錄成功铣除,則偏向鎖獲取成功谚咬,記錄鎖狀態(tài)為偏向鎖,以后當前線程等于owner就可以零成本的直接獲得鎖尚粘;否則择卦,說明有其他線程競爭,膨脹為輕量級鎖郎嫁。

偏向鎖無法使用自旋鎖優(yōu)化秉继,因為一旦有其他線程申請鎖,就破壞了偏向鎖的假定泽铛。

缺點

同樣的尚辑,如果明顯存在其他線程申請鎖,那么偏向鎖將很快膨脹為輕量級鎖盔腔。

不過這個副作用已經小的多杠茬。

如果需要,使用參數-XX:-UseBiasedLocking禁止偏向鎖優(yōu)化(默認打開)弛随。

小結

偏向鎖瓢喉、輕量級鎖、重量級鎖分配和膨脹的詳細過程見后舀透。會涉及一些Mark Word與CAS的知識栓票。

偏向鎖、輕量級鎖愕够、重量級鎖適用于不同的并發(fā)場景:

  • 偏向鎖:無實際競爭走贪,且將來只有第一個申請鎖的線程會使用鎖。
  • 輕量級鎖:無實際競爭惑芭,多個線程交替使用鎖坠狡;允許短時間的鎖競爭。
  • 重量級鎖:有實際競爭强衡,且鎖競爭時間長擦秽。

另外,如果鎖競爭時間短漩勤,可以使用自旋鎖進一步優(yōu)化輕量級鎖、重量級鎖的性能缩搅,減少線程切換越败。

如果鎖競爭程度逐漸提高(緩慢),那么從偏向鎖逐步膨脹到重量鎖硼瓣,能夠提高系統(tǒng)的整體性能究飞。

鎖分配和膨脹過程

重申置谦,這部分主要是根據網上的多方資料整理。核心是這位巨巨整理的流程圖亿傅,相當詳細媒峡,基本符合邏輯。

前面講述了內置鎖在使用過程中的一些基本問題和解決方案葵擎,實現原理一筆帶過谅阿。詳細的鎖分配和膨脹過程如下:

image.png

圖中有一處疑問:

按照圖中流程,如果發(fā)現鎖已經膨脹為重量級鎖酬滤,就直接使用互斥量mutex阻塞當前線程签餐。

然而,自旋鎖的一大好處就是減少線程切換的開銷盯串。在這里沒有必要直接阻塞當前線程氯檐,大可以像輕量級鎖一樣,自旋一會体捏,失敗了再阻塞冠摄。

特別說明兩點:

  • CAS記錄owner時,expected == null几缭,newValue == ownerThreadId河泳,因此,只有第一個申請偏向鎖的線程能夠返回成功奏司,后續(xù)線程都必然失敗(部分線程檢測到可偏向乔询,同時嘗試CAS記錄owner)。
  • 內置鎖只能沿著偏向鎖韵洋、輕量級鎖竿刁、重量級鎖的順序逐漸膨脹,不能“收縮”搪缨。這基于JVM的另一個假定食拜,“一旦破壞了上一級鎖的假定,就認為該假定以后也必不成立”副编。

另外负甸,當重量級鎖被解除后,需要喚醒一個被阻塞的線程痹届,這部分邏輯與ReentrantLock基本相同呻待,詳見源碼|并發(fā)一枝花之ReentrantLock與AQS(1):lock、unlock队腐。

簡化版

上圖記載的很詳細蚕捉,也有Mark Word的圖解〔裉裕看懂上圖后迫淹,再來看《深入理解Java虛擬機:JVM高級特性與最佳實踐(第2版)》中的簡化版流程圖就能看懂了:

image.png

挖坑:

簡化版中指出了重偏向過程秘通。這一過程對于性能優(yōu)化和膨脹過程都非常重要;但如果考慮重偏向的話敛熬,可能上述特別說明的內容就不成立了肺稀。要整理的筆記太多啦時間不夠啊,猴子選擇暫時放棄這個問題应民,话原,,恩瑞妇,挖個坑稿静,以后再追源碼填坑。

重偏向和epoch的作用參考:


參考:


本文鏈接:淺談偏向鎖、輕量級鎖蔓倍、重量級鎖
作者:猴子007
出處:https://monkeysayhi.github.io
本文基于 知識共享署名-相同方式共享 4.0 國際許可協(xié)議發(fā)布悬钳,歡迎轉載,演繹或用于商業(yè)目的偶翅,但是必須保留本文的署名及鏈接默勾。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市聚谁,隨后出現的幾起案子母剥,更是在濱河造成了極大的恐慌,老刑警劉巖形导,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件环疼,死亡現場離奇詭異,居然都是意外死亡朵耕,警方通過查閱死者的電腦和手機炫隶,發(fā)現死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來阎曹,“玉大人伪阶,你說我怎么就攤上這事〈ο樱” “怎么了栅贴?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長熏迹。 經常有香客問我筹误,道長,這世上最難降的妖魔是什么癣缅? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任厨剪,我火速辦了婚禮,結果婚禮上友存,老公的妹妹穿的比我還像新娘祷膳。我一直安慰自己,他們只是感情好屡立,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布直晨。 她就那樣靜靜地躺著,像睡著了一般膨俐。 火紅的嫁衣襯著肌膚如雪勇皇。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天焚刺,我揣著相機與錄音敛摘,去河邊找鬼。 笑死乳愉,一個胖子當著我的面吹牛兄淫,可吹牛的內容都是我干的。 我是一名探鬼主播蔓姚,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼捕虽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了坡脐?” 一聲冷哼從身側響起泄私,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤备闲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后浅役,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡觉既,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年惧盹,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞪讼。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖符欠,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情希柿,我是刑警寧澤诊沪,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布养筒,位于F島的核電站,受9級特大地震影響端姚,放射性物質發(fā)生泄漏。R本人自食惡果不足惜渐裸,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望昏鹃。 院中可真熱鬧,春花似錦洞渤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宪巨。三九已至,卻和暖如春捏卓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背怠晴。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蒜田,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓美莫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親梯捕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容