6 ReentrantLock

ReentrantLock,可重入鎖锋边,是一種遞歸無阻塞的同步機(jī)制拆内。它可以等同于synchronized的使用,但是ReentrantLock提供了比synchronized更強(qiáng)大宠默、靈活的鎖機(jī)制麸恍,可以減少死鎖發(fā)生的概率。
API介紹如下:

一個(gè)可重入的互斥鎖定 Lock搀矫,它具有與使用 synchronized 方法和語句所訪問的隱式監(jiān)視器鎖定相同的一些基本行為和語義抹沪,但功能更強(qiáng)大。ReentrantLock 將由最近成功獲得鎖定瓤球,并且還沒有釋放該鎖定的線程所擁有融欧。當(dāng)鎖定沒有被另一個(gè)線程所擁有時(shí),調(diào)用 lock 的線程將成功獲取該鎖定并返回卦羡。如果當(dāng)前線程已經(jīng)擁有該鎖定噪馏,此方法將立即返回÷潭可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法來檢查此情況是否發(fā)生欠肾。

ReentrantLock還提供了公平鎖非公平鎖的選擇,構(gòu)造方法接受一個(gè)可選的公平參數(shù)(默認(rèn)非公平鎖)拟赊,當(dāng)設(shè)置為true時(shí)刺桃,表示公平鎖,否則為非公平鎖吸祟。
公平鎖與非公平鎖的區(qū)別在于公平鎖的鎖獲取是有順序的瑟慈。但是公平鎖的效率往往沒有非公平鎖的效率高,在許多線程訪問的情況下屋匕,公平鎖表現(xiàn)出較低的吞吐量葛碧。

獲取鎖

我們一般都是這么使用ReentrantLock獲取鎖的:

//非公平鎖
ReentrantLock lock = new ReentrantLock();
lock.lock();

然后看看lock()

public void lock() {
    sync.lock();
}

Sync為ReentrantLock里面的一個(gè)內(nèi)部類,它繼承AQS(AbstractQueuedSynchronizer)过吻,它有兩個(gè)子類:公平鎖FairSync和非公平鎖NonfairSync进泼。

ReentrantLock里面大部分的功能都是委托給Sync來實(shí)現(xiàn)的,同時(shí)Sync內(nèi)部定義了lock()抽象方法由其子類去實(shí)現(xiàn),默認(rèn)實(shí)現(xiàn)了nonfairTryAcquire(int acquires)方法缘琅,可以看出它是非公平鎖的默認(rèn)實(shí)現(xiàn)方式粘都。下面我們看非公平鎖的lock()方法:

final void lock() {
    //嘗試獲取鎖
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        //獲取失敗廓推,調(diào)用AQS的acquire(int arg)方法
        acquire(1);
}

首先會(huì)第一次嘗試快速獲取鎖刷袍,如果獲取失敗,則調(diào)用acquire(int arg)方法樊展,該方法定義在AQS中呻纹,如下:

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

這個(gè)方法首先調(diào)用tryAcquire(int arg)方法,在AQS中講述過专缠,tryAcquire(int arg)需要自定義同步組件提供實(shí)現(xiàn)雷酪,非公平鎖實(shí)現(xiàn)如下:

protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

final boolean nonfairTryAcquire(int acquires) {
    //當(dāng)前線程
    final Thread current = Thread.currentThread();
    //獲取同步狀態(tài)
    int c = getState();
    //state == 0,表示沒有該鎖處于空閑狀態(tài)
    if (c == 0) {
        //獲取鎖成功,設(shè)置為當(dāng)前線程所有
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //線程重入
    //判斷鎖持有的線程是否為當(dāng)前線程
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

該方法主要邏輯:首先判斷同步狀態(tài)state == 0 ?涝婉,如果是表示該鎖還沒有被線程持有哥力,直接通過CAS獲取同步狀態(tài),如果成功返回true墩弯。如果state != 0吩跋,則判斷當(dāng)前線程是否為獲取鎖的線程,如果是則獲取鎖渔工,成功返回true锌钮。成功獲取鎖的線程再次獲取鎖,這是增加了同步狀態(tài)state引矩。

釋放鎖

獲取同步鎖后梁丘,使用完畢則需要釋放鎖,ReentrantLock提供了unlock釋放鎖:

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

unlock內(nèi)部使用Sync的release(int arg)釋放鎖旺韭,release(int arg)是在AQS中定義的:

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

與獲取同步狀態(tài)的acquire(int arg)方法相似氛谜,釋放同步狀態(tài)的tryRelease(int arg)同樣是需要自定義同步組件自己實(shí)現(xiàn):

protected final boolean tryRelease(int releases) {
    //減掉releases
    int c = getState() - releases;
    //如果釋放的不是持有鎖的線程,拋出異常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    //state == 0 表示已經(jīng)釋放完全了区端,其他線程可以獲取同步狀態(tài)了
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

只有當(dāng)同步狀態(tài)徹底釋放后該方法才會(huì)返回true混蔼。當(dāng)state == 0 時(shí),則將鎖持有線程設(shè)置為null珊燎,free= true惭嚣,表示釋放成功。

公平鎖與非公平鎖

公平鎖與非公平鎖的區(qū)別在于獲取鎖的時(shí)候是否按照FIFO的順序來悔政。釋放鎖不存在公平性和非公平性晚吞,上面以非公平鎖為例,下面我們來看看公平鎖的tryAcquire(int arg):

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

比較非公平鎖和公平鎖獲取同步狀態(tài)的過程谋国,會(huì)發(fā)現(xiàn)兩者唯一的區(qū)別就在于公平鎖在獲取同步狀態(tài)時(shí)多了一個(gè)限制條件:hasQueuedPredecessors()槽地,定義如下:

public final boolean hasQueuedPredecessors() {
    Node t = tail;  //尾節(jié)點(diǎn)
    Node h = head;  //頭節(jié)點(diǎn)
    Node s;
    
    //頭節(jié)點(diǎn) != 尾節(jié)點(diǎn)
    //同步隊(duì)列第一個(gè)節(jié)點(diǎn)不為null
    //當(dāng)前線程是同步隊(duì)列第一個(gè)節(jié)點(diǎn)
    return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
}

該方法主要做一件事情:主要是判斷當(dāng)前線程是否位于CLH同步隊(duì)列中的第一個(gè)。如果是則返回true,否則返回false捌蚊。

公平鎖和非公平鎖的區(qū)別

  1. 公平鎖每次獲取到鎖為同步隊(duì)列中的第一個(gè)節(jié)點(diǎn)集畅,保證請(qǐng)求資源時(shí)間上的絕對(duì)順序,而非公平鎖有可能剛釋放鎖的線程下次繼續(xù)獲取該鎖缅糟,則有可能導(dǎo)致其他線程永遠(yuǎn)無法獲取到鎖挺智,造成“饑餓”現(xiàn)象。

  2. 公平鎖為了保證時(shí)間上的絕對(duì)順序窗宦,需要頻繁的上下文切換赦颇,而非公平鎖會(huì)降低一定的上下文切換,降低性能開銷赴涵。因此媒怯,ReentrantLock默認(rèn)選擇的是非公平鎖,則是為了減少一部分上下文切換髓窜,保證了系統(tǒng)更大的吞吐量扇苞。

ReentrantLock與synchronized的區(qū)別

ReentrantLock是Lock的實(shí)現(xiàn)類,是一個(gè)互斥的同步器寄纵,在多線程高競爭條件下鳖敷,ReentrantLock比synchronized有更加優(yōu)異的性能表現(xiàn)。

  1. 用法比較
  • Lock使用起來比較靈活擂啥,但是必須有釋放鎖的配合動(dòng)作
  • Lock必須手動(dòng)獲取與釋放鎖哄陶,而synchronized不需要手動(dòng)釋放和開啟鎖
  • Lock只適用于代碼塊鎖,而synchronized可用于修飾方法哺壶、代碼塊等
  1. 特性比較
    ReentrantLock的優(yōu)勢(shì)體現(xiàn)在:
    具備嘗試非阻塞地獲取鎖的特性:當(dāng)前線程嘗試獲取鎖屋吨,如果這一時(shí)刻鎖沒有被其他線程獲取到,則成功獲取并持有鎖
    能被中斷地獲取鎖的特性:與synchronized不同山宾,獲取到鎖的線程能夠響應(yīng)中斷至扰,當(dāng)獲取到鎖的線程被中斷時(shí),中斷異常將會(huì)被拋出资锰,同時(shí)鎖會(huì)被釋放
    超時(shí)獲取鎖的特性:在指定的時(shí)間范圍內(nèi)獲取鎖敢课;如果截止時(shí)間到了仍然無法獲取鎖,則返回

3 注意事項(xiàng)
在使用ReentrantLock類的時(shí)绷杜,一定要注意三點(diǎn):
在finally中釋放鎖直秆,目的是保證在獲取鎖之后,最終能夠被釋放
不要將獲取鎖的過程寫在try塊內(nèi)鞭盟,因?yàn)槿绻讷@取鎖時(shí)發(fā)生了異常圾结,異常拋出的同時(shí),也會(huì)導(dǎo)致鎖無故被釋放齿诉。
ReentrantLock提供了一個(gè)newCondition的方法筝野,以便用戶在同一鎖的情況下可以根據(jù)不同的情況執(zhí)行等待或喚醒的動(dòng)作晌姚。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市歇竟,隨后出現(xiàn)的幾起案子挥唠,更是在濱河造成了極大的恐慌,老刑警劉巖焕议,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宝磨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡号坡,警方通過查閱死者的電腦和手機(jī)懊烤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門梯醒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宽堆,“玉大人,你說我怎么就攤上這事茸习⌒罅ィ” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵号胚,是天一觀的道長籽慢。 經(jīng)常有香客問我,道長猫胁,這世上最難降的妖魔是什么箱亿? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮弃秆,結(jié)果婚禮上届惋,老公的妹妹穿的比我還像新娘。我一直安慰自己菠赚,他們只是感情好脑豹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著衡查,像睡著了一般瘩欺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拌牲,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天俱饿,我揣著相機(jī)與錄音,去河邊找鬼塌忽。 笑死拍埠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的砚婆。 我是一名探鬼主播械拍,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼突勇,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了坷虑?” 一聲冷哼從身側(cè)響起甲馋,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎迄损,沒想到半個(gè)月后定躏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芹敌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年痊远,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氏捞。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡碧聪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出液茎,到底是詐尸還是另有隱情逞姿,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布捆等,位于F島的核電站滞造,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏栋烤。R本人自食惡果不足惜谒养,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望明郭。 院中可真熱鬧买窟,春花似錦、人聲如沸达址。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沉唠。三九已至疆虚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間满葛,已是汗流浹背径簿。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嘀韧,地道東北人篇亭。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像锄贷,于是被迫代替她去往敵國和親译蒂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子曼月,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

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