ReentrantLock實現(xiàn)原理分析

java.util.concurrent包中的工具實現(xiàn)核心都是AQS截亦,了解ReentrantLock的實現(xiàn)原理,需要先分析AQS以及AQS與ReentrantLock的關(guān)系雄家。

image.png
  • 同步控制中主要使用到的信息如上圖所示癣猾。AQS可以被當做是一個同步監(jiān)視器的實現(xiàn)彼绷,并且具有排隊功能。當線程嘗試獲取AQS的鎖時斋配,如果AQS已經(jīng)被別的線程獲取鎖孔飒,那么將會新建一個Node節(jié)點,并且加入到AQS的等待隊列中艰争,這個隊列也由AQS本身自己維護坏瞄。當鎖被釋放時,喚醒下一個節(jié)點嘗試獲取鎖甩卓。

  • 變量exclusiveOwnerThread在互斥模式下鸠匀,表示當前持有鎖的線程。

  • 變量tail指向等待獲取AQS的鎖的節(jié)點隊列的最后一個

  • 變量head指向隊列中head節(jié)點逾柿,head節(jié)點信息為空缀棍,不表示任何正在等待的線程。

  • 變量state表示AQS同步器的狀態(tài)机错,在不同情況下含義可能不太一樣爬范,例如以下幾種

    • 在ReentrantLock中,表示AQS的鎖是否已經(jīng)被占用獲取毡熏,0:沒有坦敌,>=1:
      已被獲取,當大于1時表示被同一線程多次重入鎖。

    • 在CountDownLatch中痢法,表示計數(shù)還剩的次數(shù)狱窘,當?shù)竭_0時,喚醒等待線程财搁。

    • 在Semaphore中蘸炸,表示AQS還可以被獲取鎖的次數(shù),獲取一次就減1尖奔,當?shù)竭_0時搭儒,嘗試獲取的線程將會阻塞.

二者關(guān)聯(lián)

ReentrantLock實現(xiàn)核心是基于AQS,看下面一張圖,分析AQS與ReentrantLock的關(guān)系提茁。
image.png

從圖中可以看出淹禾,ReentrantLock里面有最終兩個內(nèi)部類,F(xiàn)airSync和NonfairSync通過繼承AbstractQueuedSynchronizer的功能茴扁,來實現(xiàn)兩種同步互斥方案:公平鎖和非公平鎖铃岔。在ReentrantLock中最終lock和unlock操作,都由FairSync和NonfairSync實際完成峭火。

  • NonfairSync分析
    • 下面看一個最簡單利用ReentrantLock實現(xiàn)互斥的例子毁习。
ReentrantLock lock = new ReentrantLock();
        //嘗試獲取鎖
        lock.lock();
        //獲得鎖后執(zhí)行邏輯......
        //......
        //解鎖
        lock.unlock();
  • 在ReentrantLock的默認無參構(gòu)造方法中智嚷,創(chuàng)建的是非公平鎖
 public ReentrantLock() {
        sync = new NonfairSync();
    }
  • NonfairSync#lock

下面分析lock.lock();這句代碼是如何實現(xiàn)同步互斥的。

final void lock() {
    if (compareAndSetState(0, 1))//【step1】
        setExclusiveOwnerThread(Thread.currentThread());//【step2】
    else
        acquire(1);//【step3】
}
  • 【step1】上面有提到纺且,NonfairSync繼承自AbstractQueuedSynchronizer盏道,NonfairSync就是一個AQS,因此在步驟【1】其實就是利用CAS(一個原子性的比較并設(shè)置操作)嘗試設(shè)置AQS的state為1载碌。如果設(shè)置成功猜嘱,表示獲取鎖成功;如果設(shè)置失敗恐仑,表示state之前已經(jīng)是>=1泉坐,已經(jīng)被別的線程占用了AQS的鎖,所示無法設(shè)置state為1裳仆,稍后會把線程加入到等待隊列腕让。

  • 非公平鎖與公平鎖:對于NonfairSync非公平鎖來說,線程只要執(zhí)行l(wèi)ock請求歧斟,就會立馬嘗試獲取鎖纯丸,不會管AQS當前管理的等待隊列中有沒有正在等待的線程,這種操作是不公平的静袖,沒有先來后到觉鼻;而稍后介紹的FairSync公平鎖,則會在lock請求進行時队橙,先判斷AQS管理的等待隊列中是否已經(jīng)有正在等待的線程坠陈,有的話就是不嘗試獲取鎖,直接進入等待隊列捐康,保證了公平性仇矾。

  • 這一步需要熟悉的是CAS操作,分析一下compareAndSetState源碼解总,如下贮匕。這一步利用unsafe包的cas操作,unsafe包類似一種java留給開發(fā)者的后門花枫,可以用來直接操作內(nèi)存數(shù)據(jù)刻盐,并且保證這個操作的原子性。在下面的代碼中劳翰,stateOffset表示state比變量的內(nèi)存偏移地址敦锌,用來尋找state變量內(nèi)存位置。整個cas操作就是找到內(nèi)存中當前的state變量值佳簸,并且與expect期待值比較乙墙,如果跟期待值相同,那么表示這個值是可以修改的,此時就會對state值進行更新伶丐;如果與期待值不一樣,那么將不能進行更新操作疯特。unsafe保證了比較與設(shè)置值的過程是原子性的哗魂,在這一步不會出現(xiàn)線程安全問題。

protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
  • 【step2】操作是在設(shè)置state成功之后漓雅,表示當前線程獲取AQS鎖成功录别,需要設(shè)置AQS中的變量exclusiveOwnerThread為當前持有鎖的線程,做保存記錄邻吞。

  • 【step3】當嘗試獲取鎖失敗的時候组题,就需要進行步驟3,執(zhí)行acquire,進行再次嘗試或者線程進入等待隊列抱冷。

總結(jié)原理流程

  • 當線程1調(diào)用lock方法時崔列,首先看 AQS 的 state 是否為0,如果是0的話旺遮,通過cas操作將state置為1赵讯,并且設(shè)置獨占線程為當前線程

  • 如果這時候線程1 要調(diào)用另外一個lock方法,那么線程1會發(fā)現(xiàn) state = 1耿眉,它再去看獨占線程是不是就是自己边翼,如果是的話 state + 1 ,獲取鎖成功鸣剪。

  • 如果線程1 執(zhí)行的方法還沒有完成即鎖還沒有釋放组底,此時線程2調(diào)用lock方法,由于線程1沒有釋放鎖筐骇,那么state不會等于0驱负,且獨占線程是線程1而不是自己(線程2)留美,所以AQS會把線程2放到等待隊列的尾部,如果線程2的前置結(jié)點是頭結(jié)點head,那么線程2會通過死循環(huán)一直去獲取鎖般甲,如果還是獲取不到鎖,那么會阻塞住線程2彼水,下面的圖有點問題啊条霜。如果不是頭結(jié)點那么就會阻塞線程2,等待線程1釋放鎖且喚醒它悔详。

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末镊屎,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子茄螃,更是在濱河造成了極大的恐慌缝驳,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異用狱,居然都是意外死亡运怖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門夏伊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來摇展,“玉大人,你說我怎么就攤上這事溺忧∮搅” “怎么了?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵鲁森,是天一觀的道長祟滴。 經(jīng)常有香客問我,道長歌溉,這世上最難降的妖魔是什么垄懂? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮痛垛,結(jié)果婚禮上埠偿,老公的妹妹穿的比我還像新娘。我一直安慰自己榜晦,他們只是感情好冠蒋,可當我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著乾胶,像睡著了一般抖剿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上识窿,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天斩郎,我揣著相機與錄音,去河邊找鬼喻频。 笑死缩宜,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的甥温。 我是一名探鬼主播锻煌,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼姻蚓!你這毒婦竟也來了宋梧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤狰挡,失蹤者是張志新(化名)和其女友劉穎捂龄,沒想到半個月后释涛,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡倦沧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年唇撬,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片展融。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡局荚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出愈污,到底是詐尸還是另有隱情,我是刑警寧澤轮傍,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布暂雹,位于F島的核電站,受9級特大地震影響创夜,放射性物質(zhì)發(fā)生泄漏杭跪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一驰吓、第九天 我趴在偏房一處隱蔽的房頂上張望涧尿。 院中可真熱鬧,春花似錦檬贰、人聲如沸姑廉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽桥言。三九已至,卻和暖如春葵礼,著一層夾襖步出監(jiān)牢的瞬間号阿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工鸳粉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留扔涧,地道東北人。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓届谈,卻偏偏與公主長得像枯夜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子艰山,可洞房花燭夜當晚...
    茶點故事閱讀 44,947評論 2 355

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