ReentrantLock的原理

ReentrantLock實(shí)現(xiàn)了Lock接口堆生,提供了lock虐译、trylock泉唁、unlock等方法鹅龄。這些方法通過(guò)AQS同步器來(lái)管理鎖狀態(tài),實(shí)現(xiàn)加鎖和解鎖亭畜。ReentrantLock包含幾個(gè)特性:公平鎖扮休、可重入、非阻塞獲取鎖拴鸵、可中斷等玷坠,下面來(lái)看看這些特性是如何實(shí)現(xiàn)的蜗搔。

先簡(jiǎn)單介紹下AQS,它是并發(fā)包里鎖管理的核心八堡,包含幾個(gè)重要屬性:

  1. state字段樟凄,鎖計(jì)數(shù)器來(lái)記錄鎖的狀態(tài)
  2. thread字段,擁有鎖的線(xiàn)程
  3. Node兄渺,Node包含pre和next指針缝龄,實(shí)現(xiàn)了CLH等待隊(duì)列

公平性

ReentrantLock中通過(guò)繼承Sync實(shí)現(xiàn)了公平(FairSync)和非公平(NonfairSync)兩種同步器。主要區(qū)別體現(xiàn)在lock方法上溶耘,lock方法會(huì)調(diào)用tryAcquire方法二拐,去獲取鎖。公平和非公平同步器獲取鎖的方式就相差hasQueuedPredecessors()這一個(gè)方法凳兵。

//公平鎖tryAcquire方法片段
if (c == 0) {
    if (!hasQueuedPredecessors() && //非公平鎖沒(méi)有!hasQueuedPredecessors()這個(gè)條件
        compareAndSetState(0, acquires)) {
        setExclusiveOwnerThread(current);
        return true;
    }
}

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;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

hasQueuedPredecessors()方法是判斷百新,等待隊(duì)列上是否有非當(dāng)前線(xiàn)程符合鎖獲取條件。
所以公平鎖實(shí)現(xiàn)是先判斷鎖沒(méi)有被獲取庐扫,且等待隊(duì)列沒(méi)有其他 符合鎖獲取條件的線(xiàn)程在等待饭望,那么當(dāng)前線(xiàn)程才嘗試去獲取鎖。而非公平鎖是不管有沒(méi)有線(xiàn)程在等待形庭,都直接去嘗試獲取鎖铅辞。

符合鎖獲取條件:等待線(xiàn)程處于等待隊(duì)列的第二個(gè),第一個(gè)是代表當(dāng)前獲取鎖的線(xiàn)程萨醒,第二個(gè)代表下一個(gè)可以獲取鎖的線(xiàn)程

可重入

可重入性也是體現(xiàn)在加鎖的時(shí)候斟珊。加鎖時(shí),如果鎖已經(jīng)被占用富纸,當(dāng)前線(xiàn)程會(huì)判斷占有鎖的線(xiàn)程是不是自己囤踩。如果是那么再次進(jìn)入鎖,鎖計(jì)數(shù)器(state)加n晓褪。

if (c == 0) {
    if (compareAndSetState(0, acquires)) {
        setExclusiveOwnerThread(current);
        return true;
    }
}
//可重入性
else if (current == getExclusiveOwnerThread()) {
    int nextc = c + acquires;
    if (nextc < 0) // overflow
        throw new Error("Maximum lock count exceeded");
    setState(nextc);
    return true;
}

非阻塞獲取鎖

通過(guò)tryLock方法堵漱,可以實(shí)現(xiàn)非阻塞獲取鎖。獲取鎖時(shí)涣仿,如果鎖沒(méi)被占用勤庐,則通過(guò)compareAndSetState更新鎖狀態(tài)為1代表被占用,更新失敗再判斷是否可重入好港,都失敗那么直接返回愉镰,并不會(huì)阻塞等待。
如果是設(shè)置了超時(shí)的tryLock钧汹,在獲取鎖失敗后加入等待隊(duì)列岛杀,并通過(guò)LockSupport.parkNanos方法使線(xiàn)程進(jìn)入有限時(shí)間的阻塞,線(xiàn)程沒(méi)被喚醒或者阻塞時(shí)間到崭孤,則獲取鎖失敗类嗤。

可中斷

一般情況ReentrantLock在獲取鎖的時(shí)候糊肠,如果線(xiàn)程被中斷,中斷信息并不會(huì)被拋出來(lái)遗锣,如果通過(guò)tryInterruptibly方法獲取鎖货裹,就可以捕獲到中斷異常,終止鎖獲取行為精偿。

//tryInterruptibly實(shí)際調(diào)用該方法
public final void acquireInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    //獲取鎖失敗
    if (!tryAcquire(arg))
        doAcquireInterruptibly(arg);
}

private void doAcquireInterruptibly(int arg)
        throws InterruptedException {
    final Node node = addWaiter(Node.EXCLUSIVE);
    boolean failed = true;
    try {
        for (;;) {
            //判斷當(dāng)前線(xiàn)程是否符合所獲取條件弧圆,嘗試獲取鎖
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                //拋出線(xiàn)程中斷異常
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

doAcquireInterruptibly方法先嘗試獲取鎖,如果獲取失敗笔咽,則進(jìn)入等待隊(duì)列搔预,線(xiàn)程等待被喚醒或者被中斷。如果是被中斷信息叶组,會(huì)重新拋出一個(gè)InterruptedException異常拯田,導(dǎo)致鎖獲取行為中斷。

關(guān)于鎖的釋放

ReentrantLock是可重入的獨(dú)占鎖甩十,對(duì)于鎖的釋放有點(diǎn)特殊船庇。每次重入鎖時(shí),都會(huì)在同步器的計(jì)數(shù)器上加1侣监,而調(diào)用unlock釋放鎖時(shí)鸭轮,是對(duì)計(jì)數(shù)器減1,所以重入多次數(shù)和釋放次數(shù)要一樣橄霉,計(jì)數(shù)器為0后鎖才能被其他線(xiàn)程使用窃爷。

public void unlock() {
    sync.release(1);
}

public final boolean release(int arg) {
    //如果鎖釋放成功,就更新head節(jié)點(diǎn)狀態(tài)姓蜂,并喚醒下一個(gè)node
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    //計(jì)數(shù)器為0時(shí)按厘,返回值free才成功
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市覆糟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌遮咖,老刑警劉巖滩字,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異御吞,居然都是意外死亡麦箍,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)陶珠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)挟裂,“玉大人,你說(shuō)我怎么就攤上這事揍诽【魅兀” “怎么了栗竖?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)渠啤。 經(jīng)常有香客問(wèn)我狐肢,道長(zhǎng),這世上最難降的妖魔是什么沥曹? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任份名,我火速辦了婚禮,結(jié)果婚禮上妓美,老公的妹妹穿的比我還像新娘僵腺。我一直安慰自己,他們只是感情好壶栋,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布辰如。 她就那樣靜靜地躺著,像睡著了一般委刘。 火紅的嫁衣襯著肌膚如雪丧没。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天锡移,我揣著相機(jī)與錄音呕童,去河邊找鬼。 笑死淆珊,一個(gè)胖子當(dāng)著我的面吹牛夺饲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播施符,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼往声,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了戳吝?” 一聲冷哼從身側(cè)響起浩销,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎听哭,沒(méi)想到半個(gè)月后慢洋,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡陆盘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年普筹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片隘马。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡太防,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出酸员,到底是詐尸還是另有隱情蜒车,我是刑警寧澤讳嘱,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站醇王,受9級(jí)特大地震影響呢燥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜寓娩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一叛氨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧棘伴,春花似錦寞埠、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至阱穗,卻和暖如春饭冬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背揪阶。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工昌抠, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鲁僚。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓炊苫,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親冰沙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子侨艾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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

  • ReentrantLock 介紹 一個(gè)可重入的互斥鎖,它具有與使用{synchronized}方法和語(yǔ)句訪問(wèn)的隱式...
    tomas家的小撥浪鼓閱讀 4,040評(píng)論 1 4
  • 一拓挥、多線(xiàn)程 說(shuō)明下線(xiàn)程的狀態(tài) java中的線(xiàn)程一共有 5 種狀態(tài)唠梨。 NEW:這種情況指的是,通過(guò) New 關(guān)鍵字創(chuàng)...
    Java旅行者閱讀 4,659評(píng)論 0 44
  • 天黑了 她抬起頭望了望 裹緊了自己 往回家路上快步走 一個(gè)人的路途總是孤獨(dú)的 各種各樣的畫(huà)面突然出現(xiàn)在了腦海 她嘆...
    她是你不是我閱讀 153評(píng)論 0 0
  • 一個(gè)日思夜想的男朋友 每天都很有動(dòng)力的起床侥啤,背書(shū)当叭,跟他分享 他不在時(shí)候的每一件事 每晚睡前的視頻電話(huà)是最開(kāi)心的時(shí)候...
    uaremybelief閱讀 235評(píng)論 0 0
  • 01 SK老師的公益巡講來(lái)洛陽(yáng)糠雨,講NLP智慧溝通。了解課程的第一天徘跪,就開(kāi)始了盤(pán)算甘邀。愛(ài)人端午前后放假琅攘,請(qǐng)爸媽照顧孩子...
    白蘭姑娘閱讀 227評(píng)論 2 2