ReentrantLock-重入鎖源碼分析

ReentrantLock

重入鎖, 表示該鎖支持一個線程對資源的重復(fù)加鎖

類結(jié)構(gòu)

首先讓我們先看下 ReentrantLock 的類結(jié)構(gòu)如下圖所示:

image

從圖中我們可以看出 ReentrantLock 實(shí)現(xiàn) Lock 接口饰剥,同時內(nèi)部類 Sync 是 AQS 的子類蠢沿;而 Sync 又有兩個子類 NonfairSync 和 FairSync 分別對應(yīng)非公平和公平鎖兩種策略搜贤。

構(gòu)造

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

ReentrantLock 默認(rèn)采用非公平的策略跟匆,也可以在構(gòu)造的時候指定是否公平的策略。

非公平鎖

非公平鎖是指在競爭獲取鎖的過程中兔仰,有可能后來者居上

lock() 獲取鎖
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * Performs lock.  Try immediate barge, backing up to normal
     * acquire on failure.
     */
    final void lock() {
        // CAS 設(shè)置 state 值為 1
        if (compareAndSetState(0, 1))
            // CAS 成功則說明獲取到鎖, 此時將當(dāng)前線程設(shè)置為獨(dú)占模式下鎖對象的持有者
            setExclusiveOwnerThread(Thread.currentThread());
        else
            // CAS 失敗
            // 可能是同一線程再次獲取鎖
            // 也可能是不同線程獲取鎖
            acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        // 調(diào)用父類 sync
        return nonfairTryAcquire(acquires);
    }
}
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
      // 此時說明已有線程釋放了鎖
      // 有可能是同步隊列里阻塞的線程被喚醒時嘗試獲取鎖
        if (compareAndSetState(0, acquires)) {
            // CAS 成功則說明獲取到鎖, 此時將當(dāng)前線程設(shè)置為獨(dú)占模式下鎖對象的持有者
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        // 說明同一線程再次獲取鎖
        // state 加 1
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

獲取鎖的過程如下 :

  • 通過 CAS 操作最疆, 設(shè)置 state = 1
  • 若 CAS 操作成功轧铁,則將當(dāng)前線程設(shè)置為獨(dú)占模式鎖對象的持有者
  • 若 CAS 操作失敗, 最終會調(diào)用 sync 的方法 nonfairTryAcquire; 此時說明可能是同一線程再次嘗試獲取鎖,也有可能是其他線程嘗試獲取鎖
  • 若當(dāng)前 state == 0, 繼續(xù)執(zhí)行前兩步操作
  • 若當(dāng)前 state != 0, 則判斷當(dāng)前線程是否為鎖的持有者泣矛;若判斷成立疲眷,則對 state + 1
unlock() - 釋放鎖

非公平鎖的釋放調(diào)用的是父類 sync 的 tryRelease 方法

protected final boolean tryRelease(int releases) {
    // state 減一操作
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        // 當(dāng)前線程不是當(dāng)前鎖的持有者時拋出異常
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        // 只有 state == 0 時 才是真正完成鎖的釋放
        free = true;
        // 將鎖的持有者清空
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

從釋放鎖的實(shí)現(xiàn)可以看出,獲取鎖與釋放鎖的操作是對等的乳蓄,譬如下方偽代碼:

ReentrantLock lock = new ReentrantLock();

public void do () {
  lock.lock();

  try {

    do();
    // 退出遞歸

  } finally {
    lock.unlock();
  }
}

公平鎖

公平鎖是指獲取鎖的順序完全符合請求時間的順序咪橙,也就是先到先得

lock() - 獲取鎖

接下來我們下公平鎖與非公平鎖在獲取鎖時有什么不同

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 不同于非公平鎖操作,公平鎖多了個判斷條件 hasQueuedPredecessors
        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;
}
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;
    // h != t 說明同步隊列已有等待的節(jié)點(diǎn)
    // s = h.next == null 這個有點(diǎn)沒明白; head 的后置為空應(yīng)該就是 head == tail 吧
    // s.thread != Thread.currentThread 是判斷當(dāng)前線程是不是同步隊列的首個阻塞線程 如果是是允許獲取到鎖的
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}
Queries whether any threads have been waiting to acquire longer than the current thread.

hasQueuedPredecessors 方法主要實(shí)現(xiàn)的是查找是否有等待時間超過當(dāng)前線程的其他線程, 公平鎖也就是通過該方法保證獲取鎖的有序性虚倒。

unlock() - 釋放鎖

公平鎖的釋放與非公平鎖的釋放操作一致

小結(jié)

  • ReentrantLock 如何實(shí)現(xiàn)可重入 ? (通過判斷當(dāng)前線程是否為當(dāng)前鎖對象的持有者)
  • 如何實(shí)現(xiàn)公平鎖 ? (若當(dāng)前同步隊列中有等待的節(jié)點(diǎn)則獲取鎖失敗)
  • 非公平鎖和公平鎖對性能有什么影響 ? (公平鎖會造成大量的線程切換美侦,非公平鎖會出現(xiàn)線程“饑餓”現(xiàn)象,但線程切換少提高吞吐量)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末魂奥,一起剝皮案震驚了整個濱河市菠剩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌耻煤,老刑警劉巖具壮,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件准颓,死亡現(xiàn)場離奇詭異,居然都是意外死亡棺妓,警方通過查閱死者的電腦和手機(jī)攘已,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來怜跑,“玉大人样勃,你說我怎么就攤上這事⌒苑遥” “怎么了峡眶?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長植锉。 經(jīng)常有香客問我辫樱,道長,這世上最難降的妖魔是什么俊庇? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任狮暑,我火速辦了婚禮,結(jié)果婚禮上辉饱,老公的妹妹穿的比我還像新娘心例。我一直安慰自己,他們只是感情好鞋囊,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瞎惫,像睡著了一般溜腐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瓜喇,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天挺益,我揣著相機(jī)與錄音,去河邊找鬼乘寒。 笑死望众,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的伞辛。 我是一名探鬼主播烂翰,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蚤氏!你這毒婦竟也來了甘耿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤竿滨,失蹤者是張志新(化名)和其女友劉穎佳恬,沒想到半個月后捏境,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡毁葱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年垫言,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片倾剿。...
    茶點(diǎn)故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡筷频,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出柱告,到底是詐尸還是另有隱情截驮,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布际度,位于F島的核電站葵袭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏乖菱。R本人自食惡果不足惜坡锡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望窒所。 院中可真熱鬧鹉勒,春花似錦、人聲如沸吵取。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽皮官。三九已至脯倒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間捺氢,已是汗流浹背藻丢。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留摄乒,地道東北人悠反。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像馍佑,于是被迫代替她去往敵國和親斋否。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評論 2 359

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