Java中的鎖[原理、鎖優(yōu)化址遇、CAS熄阻、AQS]

本文作者:景小財(cái)
作者簡介:美團(tuán)外賣活動業(yè)務(wù)負(fù)責(zé)人

1、為什么要用鎖倔约?

鎖-是為了解決并發(fā)操作引起的臟讀秃殉、數(shù)據(jù)不一致的問題。

2浸剩、鎖實(shí)現(xiàn)的基本原理

2.1钾军、volatile

Java編程語言允許線程訪問共享變量, 為了確保共享變量能被準(zhǔn)確和一致地更新乒省,線程應(yīng)該確保通過排他鎖單獨(dú)獲得這個變量巧颈。Java語言提供了volatile,在某些情況下比鎖要更加方便袖扛。

volatile在多處理器開發(fā)中保證了共享變量的“ 可見性”砸泛。可見性的意思是當(dāng)一個線程修改一個共享變量時(shí)蛆封,另外一個線程能讀到這個修改的值唇礁。

image.png

結(jié)論:如果volatile變量修飾符使用恰當(dāng)?shù)脑挘萻ynchronized的使用和執(zhí)行成本更低惨篱,因?yàn)樗粫鹁€程上下文的切換和調(diào)度盏筐。

2.2、synchronized

synchronized通過鎖機(jī)制實(shí)現(xiàn)同步砸讳。

先來看下利用synchronized實(shí)現(xiàn)同步的基礎(chǔ):Java中的每一個對象都可以作為鎖琢融。

具體表現(xiàn)為以下3種形式界牡。

  • 對于普通同步方法,鎖是當(dāng)前實(shí)例對象漾抬。
  • 對于靜態(tài)同步方法宿亡,鎖是當(dāng)前類的Class對象。
  • 對于同步方法塊纳令,鎖是Synchonized括號里配置的對象挽荠。

當(dāng)一個線程試圖訪問同步代碼塊時(shí),它首先必須得到鎖平绩,退出或拋出異常時(shí)必須釋放鎖圈匆。

2.2.1 synchronized實(shí)現(xiàn)原理

synchronized是基于Monitor來實(shí)現(xiàn)同步的。

Monitor從兩個方面來支持線程之間的同步:

  • 互斥執(zhí)行
  • 協(xié)作

1捏雌、Java 使用對象鎖 ( 使用 synchronized 獲得對象鎖 ) 保證工作在共享的數(shù)據(jù)集上的線程互斥執(zhí)行跃赚。

2、使用 notify/notifyAll/wait 方法來協(xié)同不同線程之間的工作性湿。

3来累、Class和Object都關(guān)聯(lián)了一個Monitor。

Monitor 的工作機(jī)理
  • 線程進(jìn)入同步方法中窘奏。
  • 為了繼續(xù)執(zhí)行臨界區(qū)代碼,線程必須獲取 Monitor 鎖葫录。如果獲取鎖成功着裹,將成為該監(jiān)視者對象的擁有者。任一時(shí)刻內(nèi)米同,監(jiān)視者對象只屬于一個活動線程(The Owner)
  • 擁有監(jiān)視者對象的線程可以調(diào)用 wait() 進(jìn)入等待集合(Wait Set)骇扇,同時(shí)釋放監(jiān)視鎖,進(jìn)入等待狀態(tài)面粮。
  • 其他線程調(diào)用 notify() / notifyAll() 接口喚醒等待集合中的線程少孝,這些等待的線程需要重新獲取監(jiān)視鎖后才能執(zhí)行 wait() 之后的代碼。
  • 同步方法執(zhí)行完畢了熬苍,線程退出臨界區(qū)稍走,并釋放監(jiān)視鎖。

參考文檔:https://www.ibm.com/developerworks/cn/java/j-lo-synchronized

2.2.2 synchronized具體實(shí)現(xiàn)

1柴底、同步代碼塊采用monitorenter婿脸、monitorexit指令顯式的實(shí)現(xiàn)。

2柄驻、同步方法則使用ACC_SYNCHRONIZED標(biāo)記符隱式的實(shí)現(xiàn)狐树。

通過實(shí)例來看看具體實(shí)現(xiàn):

public class SynchronizedTest {
 
    public synchronized void method1(){
        System.out.println("Hello World!");
    }
 
    public  void method2(){
        synchronized (this){
            System.out.println("Hello World!");
        }
    }
}

javap編譯后的字節(jié)碼如下:

image.png

monitorenter

每一個對象都有一個monitor,一個monitor只能被一個線程擁有鸿脓。當(dāng)一個線程執(zhí)行到monitorenter指令時(shí)會嘗試獲取相應(yīng)對象的monitor抑钟,獲取規(guī)則如下:

  • 如果monitor的進(jìn)入數(shù)為0涯曲,則該線程可以進(jìn)入monitor增显,并將monitor進(jìn)入數(shù)設(shè)置為1括蝠,該線程即為monitor的擁有者。
  • 如果當(dāng)前線程已經(jīng)擁有該monitor伊诵,只是重新進(jìn)入心俗,則進(jìn)入monitor的進(jìn)入數(shù)加1傲武,所以synchronized關(guān)鍵字實(shí)現(xiàn)的鎖是可重入的鎖。
  • 如果monitor已被其他線程擁有城榛,則當(dāng)前線程進(jìn)入阻塞狀態(tài)揪利,直到monitor的進(jìn)入數(shù)為0,再重新嘗試獲取monitor狠持。

monitorexit

只有擁有相應(yīng)對象的monitor的線程才能執(zhí)行monitorexit指令疟位。每執(zhí)行一次該指令monitor進(jìn)入數(shù)減1,當(dāng)進(jìn)入數(shù)為0時(shí)當(dāng)前線程釋放monitor喘垂,此時(shí)其他阻塞的線程將可以嘗試獲取該monitor甜刻。

2.2.3 鎖存放的位置

鎖標(biāo)記存放在Java對象頭的Mark Word中。

Java對象頭長度
32位JVM Mark Word 結(jié)構(gòu)
32位JVM Mark Word 狀態(tài)變化
64位JVM Mark Word 結(jié)構(gòu)

2.2.3 synchronized的鎖優(yōu)化

JavaSE1.6為了減少獲得鎖和釋放鎖帶來的性能消耗正勒,引入了“偏向鎖”和“輕量級鎖”得院。

在JavaSE1.6中,鎖一共有4種狀態(tài)章贞,級別從低到高依次是:無鎖狀態(tài)祥绞、偏向鎖狀態(tài)、輕量級鎖狀態(tài)和重量級鎖狀態(tài)鸭限,這幾個狀態(tài)會隨著競爭情況逐漸升級蜕径。

鎖可以升級但不能降級,意味著偏向鎖升級成輕量級鎖后不能降級成偏向鎖败京。這種鎖升級卻不能降級的策略兜喻,目的是為了提高獲得鎖和釋放鎖的效率。

偏向鎖:

無鎖競爭的情況下為了減少鎖競爭的資源開銷赡麦,引入偏向鎖朴皆。

image.png

輕量級鎖:

輕量級鎖所適應(yīng)的場景是線程交替執(zhí)行同步塊的情況。

image.png

鎖粗化(Lock Coarsening):也就是減少不必要的緊連在一起的unlock泛粹,lock操作车荔,將多個連續(xù)的鎖擴(kuò)展成一個范圍更大的鎖。

鎖消除(Lock Elimination):鎖削除是指虛擬機(jī)即時(shí)編譯器在運(yùn)行時(shí)戚扳,對一些代碼上要求同步忧便,但是被檢測到不可能存在共享數(shù)據(jù)競爭的鎖進(jìn)行削除。

適應(yīng)性自旋(Adaptive Spinning):自適應(yīng)意味著自旋的時(shí)間不再固定了,而是由前一次在同一個鎖上的自旋時(shí)間及鎖的擁有者的狀態(tài)來決定珠增。如果在同一個鎖對象上超歌,自旋等待剛剛成功獲得過鎖,并且持有鎖的線程正在運(yùn)行中蒂教,那么虛擬機(jī)就會認(rèn)為這次自旋也很有可能再次成功巍举,進(jìn)而它將允許自旋等待持續(xù)相對更長的時(shí)間,比如100個循環(huán)凝垛。另一方面懊悯,如果對于某個鎖,自旋很少成功獲得過梦皮,那在以后要獲取這個鎖時(shí)將可能省略掉自旋過程炭分,以避免浪費(fèi)處理器資源。

2.2.4 鎖的優(yōu)缺點(diǎn)對比

image.png

2.3剑肯、CAS

CAS捧毛,在Java并發(fā)應(yīng)用中通常指CompareAndSwap或CompareAndSet,即比較并交換让网。

1呀忧、CAS是一個原子操作,它比較一個內(nèi)存位置的值并且只有相等時(shí)修改這個內(nèi)存位置的值為新的值溃睹,保證了新的值總是基于最新的信息計(jì)算的而账,如果有其他線程在這期間修改了這個值則CAS失敗。CAS返回是否成功或者內(nèi)存位置原來的值用于判斷是否CAS成功因篇。

2福扬、JVM中的CAS操作是利用了處理器提供的CMPXCHG指令實(shí)現(xiàn)的。

優(yōu)點(diǎn):

  • 競爭不大的時(shí)候系統(tǒng)開銷小惜犀。

缺點(diǎn):

  • 循環(huán)時(shí)間長開銷大。
  • ABA問題狠裹。
  • 只能保證一個共享變量的原子操作虽界。

3、Java中的鎖實(shí)現(xiàn)

3.1涛菠、隊(duì)列同步器(AQS)

隊(duì)列同步器AbstractQueuedSynchronizer(以下簡稱同步器)莉御,是用來構(gòu)建鎖或者其他同步組件的基礎(chǔ)框架。

3.1.1俗冻、它使用了一個int成員變量表示同步狀態(tài)礁叔。

image.png

3.1.2、通過內(nèi)置的FIFO雙向隊(duì)列來完成獲取鎖線程的排隊(duì)工作迄薄。

  • 同步器包含兩個節(jié)點(diǎn)類型的應(yīng)用琅关,一個指向頭節(jié)點(diǎn),一個指向尾節(jié)點(diǎn)讥蔽,未獲取到鎖的線程會創(chuàng)建節(jié)點(diǎn)線程安全(compareAndSetTail)的加入隊(duì)列尾部涣易。同步隊(duì)列遵循FIFO画机,首節(jié)點(diǎn)是獲取同步狀態(tài)成功的節(jié)點(diǎn)。


    image.png
  • 未獲取到鎖的線程將創(chuàng)建一個節(jié)點(diǎn)新症,設(shè)置到尾節(jié)點(diǎn)步氏。如下圖所示:

image.png
  • 首節(jié)點(diǎn)的線程在釋放鎖時(shí),將會喚醒后繼節(jié)點(diǎn)徒爹。而后繼節(jié)點(diǎn)將會在獲取鎖成功時(shí)將自己設(shè)置為首節(jié)點(diǎn)荚醒。如下圖所示:


    image.png

3.1.3、獨(dú)占式/共享式鎖獲取

獨(dú)占式:有且只有一個線程能獲取到鎖隆嗅,如:ReentrantLock界阁。</pre>

共享式:可以多個線程同時(shí)獲取到鎖,如:CountDownLatch

獨(dú)占式

  • 每個節(jié)點(diǎn)自旋觀察自己的前一節(jié)點(diǎn)是不是Header節(jié)點(diǎn)榛瓮,如果是铺董,就去嘗試獲取鎖。


    image.png
  • 獨(dú)占式鎖獲取流程:

image.png

共享式:

  • 共享式與獨(dú)占式的區(qū)別:


    image.png
  • 共享鎖獲取流程:
image.png

4禀晓、鎖的使用用例

4.1精续、ConcurrentHashMap的實(shí)現(xiàn)原理及使用

ConcurrentHashMap類圖
ConcurrentHashMap數(shù)據(jù)結(jié)構(gòu)

結(jié)論:ConcurrentHashMap使用的鎖分段技術(shù)。首先將數(shù)據(jù)分成一段一段地存儲粹懒,然后給每一段數(shù)據(jù)配一把鎖重付,當(dāng)一個線程占用鎖訪問其中一個段數(shù)據(jù)的時(shí)候,其他段的數(shù)據(jù)也能被其他線程訪問凫乖。

本文作者:景小財(cái)
作者簡介:美團(tuán)外賣活動業(yè)務(wù)負(fù)責(zé)人


歡迎關(guān)注 高廣超的簡書博客 與 收藏文章 确垫!

個人介紹:

高廣超:多年一線互聯(lián)網(wǎng)研發(fā)與架構(gòu)設(shè)計(jì)經(jīng)驗(yàn),擅長設(shè)計(jì)與落地高可用帽芽、高性能删掀、可擴(kuò)展的互聯(lián)網(wǎng)架構(gòu)。

本文首發(fā)在 高廣超的簡書博客 轉(zhuǎn)載請注明导街!

簡書博客
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末披泪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子搬瑰,更是在濱河造成了極大的恐慌款票,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泽论,死亡現(xiàn)場離奇詭異艾少,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)翼悴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門缚够,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事潮瓶√绽洌” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵毯辅,是天一觀的道長埂伦。 經(jīng)常有香客問我,道長思恐,這世上最難降的妖魔是什么沾谜? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮胀莹,結(jié)果婚禮上基跑,老公的妹妹穿的比我還像新娘。我一直安慰自己描焰,他們只是感情好媳否,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著荆秦,像睡著了一般篱竭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上步绸,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天掺逼,我揣著相機(jī)與錄音,去河邊找鬼瓤介。 笑死吕喘,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的刑桑。 我是一名探鬼主播氯质,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼祠斧!你這毒婦竟也來了闻察?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤梁肿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后觅彰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吩蔑,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年填抬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了烛芬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖赘娄,靈堂內(nèi)的尸體忽然破棺而出仆潮,到底是詐尸還是另有隱情,我是刑警寧澤遣臼,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布性置,位于F島的核電站,受9級特大地震影響揍堰,放射性物質(zhì)發(fā)生泄漏鹏浅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一屏歹、第九天 我趴在偏房一處隱蔽的房頂上張望隐砸。 院中可真熱鬧,春花似錦蝙眶、人聲如沸季希。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽式塌。三九已至,卻和暖如春霹崎,著一層夾襖步出監(jiān)牢的瞬間珊搀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工尾菇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留境析,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓派诬,卻偏偏與公主長得像劳淆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子默赂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評論 2 355

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

  • 【轉(zhuǎn)自】http://blog.csdn.net/zqz_zqz/article/details/70233767...
    lxqfirst閱讀 7,452評論 4 74
  • Java8張圖 11吊趾、字符串不變性 12宛裕、equals()方法、hashCode()方法的區(qū)別 13论泛、...
    Miley_MOJIE閱讀 3,707評論 0 11
  • Java并發(fā)編程 來自Java并發(fā)編程的藝術(shù)個人博客: http://blog.csdn.net/qq_22329...
    越長越圓閱讀 3,243評論 4 54
  • 這周完成的 1 完善古詩模塊后臺部分?jǐn)?shù)據(jù)接口實(shí)現(xiàn)機(jī)制,完善古詩活動數(shù)據(jù)統(tǒng)計(jì)異步任務(wù)實(shí)現(xiàn)機(jī)制岩榆。 2 導(dǎo)入古詩模塊相關(guān)...
    heartshapebox閱讀 222評論 0 0
  • 這是我們的一場狼戲,本狼覺得情節(jié)挺好內(nèi)容不錯了袁,所以我以小說的形式把它寫下來朗恳。(可費(fèi)了我不少工夫,打字要累死了)另外...
    星河亂狐閱讀 453評論 0 3