【Java并發(fā)編程】ReentrantLock重入鎖解析

概述

重入鎖ReentrantLock,顧名思義,就是支持重進入的鎖吐辙,它表示能夠支持一個線程對資源的重復(fù)加鎖宣决。除此之外,該鎖還支持獲取鎖時的公平和非公平選擇昏苏。Synchronized關(guān)鍵字通過獲取自增尊沸、釋放遞減的方式來隱式的支持重入,那么Reentrant是如何支持重入的呢贤惯?又是怎么實現(xiàn)公平和非公平選擇的呢洼专?接下來我們帶著這些問題來看ReentrantLock的源碼

重進入的實現(xiàn)原理

重進入是指任意線程在獲取到鎖之后,能夠再次獲取該鎖孵构,而不會因為再次獲取該鎖被阻塞屁商,該特性的實現(xiàn)主要需要解決的是一下兩個問題:

  1. 線程再次獲取鎖。鎖需要識別來嘗試獲取鎖的線程颈墅,是不是當前占有鎖的線程棒假,如果是,那么獲取成功精盅,如果不是,那么獲取失敗谜酒。
  2. 鎖的最終釋放叹俏。線程重復(fù)n次獲取了鎖,隨后在n次釋放該鎖后僻族,其他線程能夠正常獲取到鎖粘驰。鎖最終能夠正常釋放屡谐,要求鎖的獲取進行自增計數(shù),計數(shù)表示該鎖被重復(fù)獲取的次數(shù)蝌数,而鎖被釋放時愕掏,計數(shù)自減,當計數(shù)歸零時顶伞,表示該鎖已經(jīng)成功釋放饵撑。

ReentrantLock是通過組合自定義同步器來實現(xiàn)鎖的獲取和釋放的,下面我們以默認的非公平鎖源碼來深入分析下實現(xiàn)原理唆貌,非公平鎖獲取鎖的源碼如下:

final boolean nonfairTryAcquire(int acquires) {
    //獲取當前線程
    final Thread current = Thread.currentThread();
    //獲取當前同步狀態(tài)
    int c = getState();
    //如果同步狀態(tài)等于0滑潘,說明該鎖未被任何線程占用
    if (c == 0) {
        //CAS方法修改同步狀態(tài)
        if (compareAndSetState(0, acquires)) {
            //將當前線程賦值給exclusiveOwnerThread
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //否則,判斷占有該鎖的線程是不是當前線程
    else if (current == getExclusiveOwnerThread()) {
        //再次獲取锨咙,狀態(tài)值加上acquires
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

相信大家很容易看懂這段代碼语卤,該方法增加了重入鎖再次獲取同步狀態(tài)的邏輯:判斷占有鎖的線程是否為當前線程來決定獲取操作是否成功,如果是獲取鎖的線程再次請求酪刀,那么將同步狀態(tài)值增加并返回true粹舵,來表示獲取同步狀態(tài)成功。成功獲取鎖的線程再次獲取鎖骂倘,只是增加了同步狀態(tài)值眼滤,這也就要求ReentrantLock在釋放同步狀態(tài)的時候,減少同步狀態(tài)的值稠茂,其對應(yīng)的tryRelease()方法源碼如下:

protected final boolean tryRelease(int releases) {
    //同步狀態(tài)遞減
    int c = getState() - releases;
    //如果當前線程不是占有該鎖的線程柠偶,那么拋出異常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    //如果遞減后同步狀態(tài)為0,那么將釋放鎖睬关,返回true
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    //如果狀態(tài)不為0诱担,那么返回false
    setState(c);
    return free;
}

從代碼中我們可以看出,只有同步狀態(tài)為0的時候电爹,鎖才會被完全釋放蔫仙。如果該鎖被獲取了n次,那么前(n-1)次tryRelease(int releases)方法必須返回false丐箩,只有最后一次才會返回true摇邦。

公平鎖與非公平鎖

ReentrantLock支持兩種鎖:公平鎖與非公平鎖,分別對應(yīng)實現(xiàn)為FairSync和NonfairSync屎勘。公平性與否是針對獲取鎖而言的施籍,如果一個鎖是公平的,那么鎖的獲取順序就應(yīng)該符合請求的絕對時間順序概漱,也就是FIFO丑慎。 回顧上一小節(jié)中非公平鎖獲取的nonfairTryAcquire(int acquires)方法,只要CAS自旋獲取同步狀態(tài)成功,則表示當前線程獲取了鎖竿裂,不會要求FIFO玉吁,而公平鎖則不然。ReentrantLock的構(gòu)造方法為無參構(gòu)造方法時腻异,構(gòu)造的就是非公平鎖进副,源碼為:

public ReentrantLock() {
        sync = new NonfairSync();
}

而ReentrantLock還有另外一種構(gòu)造方法,有參構(gòu)造方法:

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

當傳入?yún)?shù)為true時悔常,創(chuàng)建公平鎖影斑;否則創(chuàng)建非公平鎖。下面我們來看下公平鎖的處理邏輯是怎么樣的这嚣,核心方法如下:

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    //如果當前同步狀態(tài)為0
    if (c == 0) {
        //判斷同步隊列中當前節(jié)點前面是否還有其余節(jié)點鸥昏,如果沒有,那么嘗試獲取同步狀態(tài)姐帚,成功則返回true
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //否則吏垮,判斷占有鎖的線程是否為當前線程,如果是罐旗,那么同步狀態(tài)加1膳汪,返回true。
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    //否則返回false
    return false;
}

public final boolean hasQueuedPredecessors() {
    // The correctness of this depends on head being initialized
    // before tail and on head.next being accurate if the current
    // thread is first in queue.
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    //判斷同步隊列中九秀,當前節(jié)點前面是否還有其余節(jié)點
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

從上面的源碼中我們看到遗嗽,nonfailSync中的tryAcquire方法,多調(diào)用了hasQueuedPredecessors()方法來判斷同步隊列中鼓蜒,當前節(jié)點前是否還有其余節(jié)點痹换。如果有,則說明有線程比當前線程更早請求資源都弹,根據(jù)公平性要求娇豫,當前節(jié)點獲取資源失敗畅厢;如果當前節(jié)點沒有前驅(qū)節(jié)點冯痢,才會做后續(xù)操作。

公平鎖和非公平鎖可以總結(jié)出一下幾點:

  1. 公平鎖保證了鎖的獲取按照FIFO原則框杜,每次獲取到鎖的都是同步隊列的第一個節(jié)點浦楣,保證了請求資源時間上的絕對順序;非公平鎖咪辱,有可能剛剛釋放鎖的線程立馬又獲取到鎖振劳,而有的線程會一直獲取不到鎖,這也就可能造成“饑餓”現(xiàn)象油狂。
  2. 公平鎖為了保證請求資源上的絕對順序澎迎,需要頻繁的更換線程庐杨,切換上下文,而非公平鎖則一定程度上降低了上下文的切換夹供,降低了性能開銷。所以ReentrantLock默認選擇非公平鎖仁堪,這樣做是為了減少上下文切換哮洽,保證系統(tǒng)有更大的吞吐量。

注:本文參考《Java并發(fā)編程的藝術(shù)》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末弦聂,一起剝皮案震驚了整個濱河市鸟辅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌莺葫,老刑警劉巖匪凉,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異捺檬,居然都是意外死亡再层,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門堡纬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來聂受,“玉大人,你說我怎么就攤上這事烤镐〉凹茫” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵炮叶,是天一觀的道長碗旅。 經(jīng)常有香客問我,道長镜悉,這世上最難降的妖魔是什么祟辟? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮积瞒,結(jié)果婚禮上川尖,老公的妹妹穿的比我還像新娘。我一直安慰自己茫孔,他們只是感情好叮喳,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缰贝,像睡著了一般馍悟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上剩晴,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天锣咒,我揣著相機與錄音侵状,去河邊找鬼。 笑死毅整,一個胖子當著我的面吹牛趣兄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播悼嫉,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼艇潭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了戏蔑?” 一聲冷哼從身側(cè)響起蹋凝,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎总棵,沒想到半個月后鳍寂,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡情龄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年迄汛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刃唤。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡隔心,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出尚胞,到底是詐尸還是另有隱情硬霍,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布笼裳,位于F島的核電站唯卖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏躬柬。R本人自食惡果不足惜拜轨,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望允青。 院中可真熱鬧橄碾,春花似錦、人聲如沸颠锉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽琼掠。三九已至拒垃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瓷蛙,已是汗流浹背悼瓮。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工戈毒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人横堡。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓埋市,卻偏偏與公主長得像,于是被迫代替她去往敵國和親翅萤。 傳聞我的和親對象是個殘疾皇子恐疲,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

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