Java鎖:悲觀/樂觀/阻塞/自旋/公平鎖/閉鎖,鎖消除CAS及synchronized的三種鎖級(jí)別

JAVA LOCK 大全

[TOC]

一恰画、廣義分類:樂觀鎖/悲觀鎖

1.1 樂觀鎖的實(shí)現(xiàn)CAS (Compare and Swap)

樂觀鎖適合低并發(fā)的情況宏怔,在高并發(fā)的情況下由于自旋焰宣,性能甚至可能悲觀鎖更差蒿讥。

CAS是一種算法达椰,CAS(V,E,N),V:要更新的變量 E:預(yù)期值 N:新值挂绰。

  • 如果多個(gè)線程進(jìn)行CAS操作屎篱,只有一個(gè)會(huì)成功,其余的會(huì)失斂佟(允許再次嘗試)交播。
  • CAS是樂觀鎖的一種帶自選的實(shí)現(xiàn)算法(對(duì)象和類的關(guān)系)挺尿。
  • 操作系統(tǒng)保證CAS的執(zhí)行是CPU原子指令咏删。

1.2 sun.misc.Unsafe

Java中CAS操作的執(zhí)行依賴于sun.misc.Unsafe類的方法,Unsafe中的方法都是native的栓辜。

  • (Unsafe類永高,非線程安全隧土,擁有類似C的指針操作,Java官方不建議直接使用的Unsafe類)
    //Usafe的幾個(gè)CAS方法
    public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
    public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

1.3 java.util.concurrent.atomic

并發(fā)包中的原子操作類(java.util.concurrent.atomic)命爬,在該包中提供了許多基于CAS實(shí)現(xiàn)的原子操作類曹傀。
這些方法都是基于調(diào)用Unsafe類實(shí)現(xiàn)的。

1.4 CAS的ABA問題 AtomicStampedReference&AtomicMarkableReference

  1. ABA問題是反復(fù)讀寫問題饲宛,在多個(gè)線程并行時(shí)皆愉,一個(gè)線程把1改成2,另一個(gè)線程又把2改成1的情況。

  2. CSA的ABA問題可以使用 AtomicStampedReference&AtomicMarkableReference兩個(gè)類來避免亥啦。

  3. AtomicStampedReference 是一個(gè)帶有時(shí)間戳的對(duì)象引用。在每次修改后不僅會(huì)設(shè)置新值练链,還會(huì)記錄更改的時(shí)間翔脱。當(dāng)該類設(shè)置對(duì)象時(shí)必須同時(shí)滿足時(shí)間戳和期望值才能寫入成功。避免了反復(fù)讀寫問題媒鼓。

  4. AtomicMarkableReference 是使用了一個(gè)bool值來標(biāo)記修改届吁,原理與AtomicStampedReference類似,不能避免ABA問題绿鸣,可以減少發(fā)生概率疚沐。

1.5 悲觀鎖(讀寫鎖是悲觀鎖的兩種實(shí)現(xiàn))

1.5.1 ReentrantReadWriteLock 可重入讀寫鎖

ReentrantReadWriteLock的構(gòu)造函數(shù)接受一個(gè)bool fair 用來指定是否是fair公平鎖。默認(rèn)是unfair.

    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock r = rwl.readLock();
    private final Lock w = rwl.writeLock();

使用讀寫鎖的時(shí)候,主動(dòng)加鎖(lock)潮模,一般在finally中釋放鎖(unlock)亮蛔。

1.5.2 Synchronized

經(jīng)過不斷的優(yōu)化(詳見 三、JAVA Synchronized 鎖的三種級(jí)別)擎厢,在低并發(fā)情況下性能很好究流。

二、Java鎖的兩種實(shí)現(xiàn):ReentrantLock 與 Synchronized

可重入鎖ReentrantLock 類實(shí)現(xiàn)了 Lock 动遭,它擁有與 synchronized 相同的并發(fā)性和內(nèi)存語義芬探。
添加了類似鎖投票、定時(shí)鎖等候和可中斷鎖等候的一些特性厘惦。

此外偷仿,它還提供了在激烈爭(zhēng)用情況下更佳的性能
(當(dāng)許多線程都想訪問共享資源時(shí),JVM 可以花更少的時(shí)候來調(diào)度線程宵蕉,把更多時(shí)間用在執(zhí)行線程上)

它有一個(gè)與鎖相關(guān)的獲取計(jì)數(shù)器酝静,如果擁有鎖的某個(gè)線程再次得到鎖,那么獲取計(jì)數(shù)器就加1国裳,然后鎖需要被釋放兩次才能獲得真正釋放形入。
這模仿了 synchronized 的語義:如果線程進(jìn)入由線程已經(jīng)擁有的監(jiān)控器保護(hù)的 synchronized 塊,就允許線程繼續(xù)進(jìn)行缝左,當(dāng)線程退出第二個(gè)(或者后續(xù)) synchronized 塊的時(shí)候亿遂,不釋放鎖,只有線程退出它進(jìn)入的監(jiān)控器保護(hù)的第一個(gè) synchronized 塊時(shí)渺杉,才釋放鎖蛇数。

IBM技術(shù)論壇中介紹 synchronized 和ReentrantLock的文章。(Jdk5)
文章的主要論述:synchronized 的功能集是 ReentrantLock 的子集是越。
ReentrantLock 多了:時(shí)間鎖等候耳舅、可中斷鎖等候、無塊結(jié)構(gòu)鎖、多個(gè)條件變量或者鎖投票等特性浦徊。
所以 ReentrantLock 從功能上來說完全可以取代 synchronized馏予。但是實(shí)際使用中不用這么絕對(duì)。
synchronized只有一個(gè)好處盔性,使用方便簡(jiǎn)單霞丧,不用主動(dòng)釋放鎖。

文章寫于jdk5時(shí)期冕香,jdk6給synchronized引入了偏向鎖等優(yōu)化蛹尝。性能差距越來越小。
所以除非用到ReentrantLock的獨(dú)有特性悉尾。其他情況下也可以繼續(xù)使用Synchronized.

三突那、synchronized 性能優(yōu)化:Synchronized的三種級(jí)別

無鎖、偏向构眯、輕量愕难、重量幾種級(jí)別的轉(zhuǎn)換圖如下:


sync鎖級(jí)別轉(zhuǎn)化.png

3.1 Biased Locking 偏向鎖(輕量級(jí)鎖的多線程優(yōu)化技術(shù)jdk6引入)

是Java6引入的一項(xiàng)針對(duì)輕量級(jí)鎖的多線程優(yōu)化技術(shù)。

  • 偏向鎖鸵赖,顧名思義务漩,它會(huì)偏向于第一個(gè)訪問鎖的線程,如果在運(yùn)行過程中它褪,同步鎖只有一個(gè)線程訪問饵骨,不存在多線程爭(zhēng)用的情況,則線程是不需要觸發(fā)同步的茫打,這種情況下居触,就會(huì)給線程加一個(gè)偏向鎖。
  • 如果在運(yùn)行過程中老赤,遇到了其他線程搶占鎖轮洋,則持有偏向鎖的線程會(huì)被掛起,JVM會(huì)消除它身上的偏向鎖抬旺,將鎖恢復(fù)到標(biāo)準(zhǔn)的輕量級(jí)鎖弊予。
  • 它通過消除資源無競(jìng)爭(zhēng)情況下的同步原語,進(jìn)一步提高了程序的運(yùn)行性能开财。但當(dāng)程序有大量競(jìng)爭(zhēng)情況汉柒,應(yīng)該關(guān)閉該特性。
//開啟偏向鎖
-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0
//關(guān)閉偏向鎖
-XX:-UseBiasedLocking

3.2 輕量級(jí)鎖

由偏向鎖升級(jí)责鳍,當(dāng)?shù)诙€(gè)線程加入鎖競(jìng)爭(zhēng)的時(shí)候碾褂,偏向鎖就升級(jí)為輕量級(jí)鎖。
加鎖過程:

  1. markWord鎖標(biāo)志位為無鎖狀態(tài)01時(shí)历葛,在當(dāng)前線程的棧幀中創(chuàng)建一個(gè)Lock Record 用來拷貝目前對(duì)象的markWord正塌。
  2. 拷貝成功后,JVM使用CAS嘗試將對(duì)象的markWord指向Lock Record。如果成功執(zhí)行3乓诽,失敗執(zhí)行4帜羊。
  3. 成功更新了markWord的指針后,該線程就有了該對(duì)象的鎖鸠天,會(huì)將markWord中的鎖標(biāo)志為設(shè)為00:輕量鎖逮壁。
  4. 更新失敗了,則先檢查對(duì)象的markWord是否指向該線程的棧幀(Stack里的)粮宛。如果是則其實(shí)已經(jīng)獲取鎖了,如果不是則說明多線程競(jìng)爭(zhēng)卖宠,則鎖膨脹為重量級(jí)鎖定10巍杈。

markWord存儲(chǔ)內(nèi)容(最后2bit是鎖狀態(tài)在無鎖和偏向鎖兩種狀態(tài)下,2bit前的1bit標(biāo)識(shí)是否偏向)

狀態(tài) 鎖標(biāo)志位(2bit) markWord存儲(chǔ)內(nèi)容
未鎖定 01 對(duì)象哈希碼扛伍、對(duì)象分代年齡
輕量級(jí)鎖定 00 指向鎖記錄的指針
膨脹(重量級(jí)鎖定) 10 執(zhí)行重量級(jí)鎖定的指針
GC標(biāo)記 11 空(不需要記錄信息)
可偏向 01 偏向線程ID筷畦、偏向時(shí)間戳、對(duì)象分代年齡

具體的存儲(chǔ)內(nèi)容如下:


markWord_lock.jpg

3.3 重量級(jí)鎖

重量級(jí)鎖發(fā)生在輕量鎖釋放鎖的期間刺洒,之前在獲取鎖的時(shí)候它拷貝了鎖對(duì)象頭的markWord鳖宾,在釋放鎖的時(shí)候如果它發(fā)現(xiàn)在它持有鎖的期間有其他線程來嘗試獲取鎖了,并且該線程對(duì)markWord做了修改逆航,兩者比對(duì)發(fā)現(xiàn)不一致鼎文,則切換到重量鎖。

四因俐、其他鎖:阻塞BlockingLock/自旋鎖SpinLock/公平fairLock /unfairLock/閉鎖Latch

4.1 阻塞鎖 Blocking lock

阻塞鎖會(huì)有線程切換的代價(jià)拇惋,但是阻塞鎖阻塞后不占用CPU。
阻塞鎖一般是悲觀鎖抹剩。

4.2 自旋鎖 Spin lock

  • 自旋鎖原理非常簡(jiǎn)單撑帖,如果持有鎖的線程能在很短時(shí)間內(nèi)釋放鎖資源,那么那些等待競(jìng)爭(zhēng)鎖的線程就不需要做內(nèi)核態(tài)和用戶態(tài)之間的切換進(jìn)入阻塞掛起狀態(tài)澳眷,它們只需要等一等(自旋)胡嘿,等持有鎖的線程釋放鎖后即可立即獲取鎖,這樣就避免用戶線程和內(nèi)核的切換的消耗钳踊。
  • 性能原因衷敌,一般JVM會(huì)限制自旋等待時(shí)間。
    自旋鎖一般是樂觀鎖箍土。

4.2.1 自旋鎖優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):在鎖競(jìng)爭(zhēng)不激烈的情況下逢享,占用鎖的時(shí)間非常短的代碼來說,自旋操作(cpu空轉(zhuǎn))的消耗小于線程阻塞掛起的消耗吴藻。
  • 缺點(diǎn):如果鎖競(jìng)爭(zhēng)激烈瞒爬,或者持有鎖的線程需要長(zhǎng)時(shí)間占用鎖執(zhí)行同步塊,就不適合自旋鎖,這是CPU空轉(zhuǎn)的消耗大于線程阻塞的消耗侧但。

Java線程切換的代價(jià):
Java的線程是映射到操作系統(tǒng)線程上的矢空,如果要阻塞或喚醒一個(gè)線程就需要操作系統(tǒng)介入,需要在用戶態(tài)與和心態(tài)之間切換禀横。

  • 內(nèi)核態(tài): CPU可以訪問內(nèi)存所有數(shù)據(jù), 包括外圍設(shè)備, 例如硬盤, 網(wǎng)卡. CPU也可以將自己從一個(gè)程序切換到另一個(gè)程序
  • 用戶態(tài): 只能受限的訪問內(nèi)存, 且不允許訪問外圍設(shè)備. 占用CPU的能力被剝奪, CPU資源可以被其他程序獲取

jdk1.6默認(rèn)開啟自旋鎖屁药,從JVM的層面對(duì)顯示鎖(都是悲觀鎖)做優(yōu)化,"智能"的決定自旋次數(shù)柏锄。
而樂觀鎖通過CAS實(shí)現(xiàn)酿箭,非阻塞,失敗后繼續(xù)獲取還是放棄的實(shí)現(xiàn)不確定趾娃,只能程序員從代碼層面對(duì)樂觀鎖做自旋(我稱之為自旋樂觀鎖)缭嫡。

4.3 fair/unfair

公平鎖,非公平鎖抬闷。
公平鎖維護(hù)了一個(gè)隊(duì)列妇蛀。要獲取鎖的線程來了都排隊(duì)。后續(xù)的線程按照隊(duì)列順序來獲取鎖笤成。
非公平鎖沒有維護(hù)隊(duì)列的開銷评架,沒有上下文切換的開銷,可能導(dǎo)致不公平炕泳,但是性能比fair好很多纵诞。

ReentrantLock的帶參構(gòu)造函數(shù)ReentrantLock(boolean fair)可以指定實(shí)現(xiàn)公平還是非公平鎖。默認(rèn)是非公平鎖培遵。

4.4 閉鎖 Latch

閉鎖(Latch)是一種同步工具類挣磨,可以延遲線程的進(jìn)度直到其到達(dá)終止?fàn)顟B(tài)。
閉鎖的作用相當(dāng)于一扇門:在閉鎖到達(dá)結(jié)束狀態(tài)之前荤懂,這扇門一直是關(guān)閉的茁裙,并且沒有任何線程能通過,當(dāng)?shù)竭_(dá)結(jié)束狀態(tài)時(shí)节仿,這扇門會(huì)打開并允許所有的線程通過晤锥。

Java中CountDownLatch是一種閉鎖實(shí)現(xiàn),位于concurrent包下廊宪。

4.5 鎖消除

鎖消除指的是在JVM即使編譯時(shí)矾瘾,通過運(yùn)行少下文的掃描,去除不可能存在共享資源競(jìng)爭(zhēng)的鎖箭启。
通過鎖消除壕翩,可以節(jié)省毫無意義的鎖請(qǐng)求.

比如在單線程下使用StringBuffer,其中的同步完全沒有必要,這時(shí)候JVM可以在運(yùn)行時(shí)基于逃逸分析計(jì)數(shù)傅寡,消除不必要的鎖放妈。

五北救、如何避免死鎖

死鎖是類似這樣的情況:a,b兩個(gè)線程,a持有鎖A 等待鎖B;b持有鎖B等待鎖A芜抒。a,b相互等待珍策,誰也執(zhí)行不下去。
避免死鎖的原則是

  1. 避免持有多個(gè)鎖宅倒。
  2. 如果確實(shí)需要多個(gè)鎖攘宙,所有代碼都應(yīng)該按照相同的順序去申請(qǐng)鎖。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拐迁,一起剝皮案震驚了整個(gè)濱河市蹭劈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌线召,老刑警劉巖链方,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異灶搜,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)工窍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門割卖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人患雏,你說我怎么就攤上這事鹏溯。” “怎么了淹仑?”我有些...
    開封第一講書人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵丙挽,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我匀借,道長(zhǎng)颜阐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任吓肋,我火速辦了婚禮凳怨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘是鬼。我一直安慰自己肤舞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開白布均蜜。 她就那樣靜靜地躺著李剖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪囤耳。 梳的紋絲不亂的頭發(fā)上篙顺,一...
    開封第一講書人閱讀 51,245評(píng)論 1 299
  • 那天偶芍,我揣著相機(jī)與錄音,去河邊找鬼慰安。 笑死腋寨,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的化焕。 我是一名探鬼主播萄窜,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼撒桨!你這毒婦竟也來了查刻?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤凤类,失蹤者是張志新(化名)和其女友劉穎穗泵,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谜疤,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡佃延,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了夷磕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片履肃。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖坐桩,靈堂內(nèi)的尸體忽然破棺而出尺棋,到底是詐尸還是另有隱情,我是刑警寧澤绵跷,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布膘螟,位于F島的核電站,受9級(jí)特大地震影響碾局,放射性物質(zhì)發(fā)生泄漏荆残。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一净当、第九天 我趴在偏房一處隱蔽的房頂上張望脊阴。 院中可真熱鬧,春花似錦蚯瞧、人聲如沸嘿期。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽备徐。三九已至,卻和暖如春甚颂,著一層夾襖步出監(jiān)牢的瞬間蜜猾,已是汗流浹背秀菱。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蹭睡,地道東北人衍菱。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像肩豁,于是被迫代替她去往敵國(guó)和親脊串。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

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