公平鎖和非公平鎖-ReentrantLock是如何實現(xiàn)公平旦袋、非公平的

1、什么是公平鎖與非公平鎖

公平鎖:公平鎖就是保障了多線程下各線程獲取鎖的順序埋虹,先到的線程優(yōu)先獲取鎖猜憎。
非公平鎖:非公平鎖則無法提供這個保障(先到的線程優(yōu)先獲取鎖)。



2搔课、ReentrantLock如何實現(xiàn)公平與非公平

Java并發(fā)包下面的ReentrantLock胰柑、ReadWriteLock默認(rèn)都是非公平模式

下面我們就來一起看看ReentrantLock是如何實現(xiàn)公平與非公平的爬泥。

ReentrantLock實現(xiàn)了Lock接口柬讨。提供了下面2個構(gòu)造方法

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

默認(rèn)構(gòu)造的是非公平鎖NonfairSync。
NonfairSync和FairSync都是ReentrantLock的內(nèi)部類袍啡,且繼承ReentrantLock的內(nèi)部抽象類Sync踩官。

// 公平鎖
protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            // 主要區(qū)別:有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;
    }
}

// 非公平鎖
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
      // 主要區(qū)別:沒有hasQueuedPredecessors方法
        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;
    }
    return false;
}

二者的主要去別在于是否有hasQueuedPredecessors方法,我們看下hasQueuedPredecessors的源碼境输。該方法返回“隊列中是否存在一個線程(先于當(dāng)前線程)”蔗牡,如果存在話颖系,當(dāng)前線程就要加入到隊列的尾部。

* @return {@code true} if there is a queued thread preceding the
 *         current thread, and {@code false} if the current thread
 *         is at the head of the queue or the queue is empty
 * @since 1.7
 */
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());
}

網(wǎng)上有這一張很棒的圖:


公平鎖與非公平鎖

結(jié)合這張圖辩越,二者的區(qū)別更明顯嘁扼。
公平鎖就是在獲取鎖之前會先判斷等待隊列是否為空或者自己是否位于隊列頭部,該條件通過才能繼續(xù)獲取鎖黔攒。

在結(jié)合兔子喝水的圖分析趁啸,非公平鎖獲取所得順序基本決定在9、10督惰、11這三個事件發(fā)生的先后順序不傅,

  • 1、若在釋放鎖的時候總是沒有新的兔子來打擾赏胚,則非公平鎖等于公平鎖访娶;
  • 2、若釋放鎖的時候栅哀,正好一個兔子來喝水震肮,而此時位于隊列頭的兔子還沒有被喚醒(因為線程上下文切換是需要不少開銷的)称龙,此時后來的兔子則優(yōu)先獲得鎖留拾,成功打破公平,成為非公平鎖鲫尊;

其實對于非公平鎖痴柔,只要線程進入了等待隊列,隊列里面依然是FIFO的原則疫向,跟公平鎖的順序是一樣的咳蔚。因為公平鎖與非公平鎖的release()部分代碼是共用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;
}

/**
 * Wakes up node's successor, if one exists.
 */
private void unparkSuccessor(Node node) {
    /*
     * If status is negative (i.e., possibly needing signal) try
     * to clear in anticipation of signalling.  It is OK if this
     * fails or if status is changed by waiting thread.
     */
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    /*
     * Thread to unpark is held in successor, which is normally
     * just the next node.  But if cancelled or apparently null,
     * traverse backwards from tail to find the actual
     * non-cancelled successor.
     */
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread);
}



3搔驼、公平鎖與非公平鎖性能對比

非公平鎖的效率高于公平鎖:上文說到的線程切換的開銷谈火,其實就是非公平鎖效率高于公平鎖的原因,因為非公平鎖減少了線程掛起的幾率舌涨,后來的線程有一定幾率逃離被掛起的開銷糯耍。



滬漂程序員一枚。
堅持寫博客囊嘉,如果覺得還可以的話温技,給個小星星哦,你的支持就是我創(chuàng)作的動力扭粱。


推薦閱讀:
Java內(nèi)存模型-volatile的應(yīng)用(實例講解)
synchronized解決原子性-synchronized的三種應(yīng)用方式(實例講解)
線程池-一文弄懂Java里面的線程池ThreadPoolExecutor
可重入鎖-面試題:synchronized是可重入鎖嗎

本文參考:一張圖讀懂非公平鎖與公平鎖

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末舵鳞,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子琢蛤,更是在濱河造成了極大的恐慌蜓堕,老刑警劉巖抛虏,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異套才,居然都是意外死亡嘉蕾,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門霜旧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來错忱,“玉大人,你說我怎么就攤上這事挂据∫郧澹” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵崎逃,是天一觀的道長掷倔。 經(jīng)常有香客問我,道長个绍,這世上最難降的妖魔是什么勒葱? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮巴柿,結(jié)果婚禮上凛虽,老公的妹妹穿的比我還像新娘。我一直安慰自己广恢,他們只是感情好凯旋,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著钉迷,像睡著了一般至非。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上糠聪,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天荒椭,我揣著相機與錄音,去河邊找鬼舰蟆。 笑死趣惠,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的夭苗。 我是一名探鬼主播信卡,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼题造!你這毒婦竟也來了傍菇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤界赔,失蹤者是張志新(化名)和其女友劉穎丢习,沒想到半個月后牵触,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡咐低,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年揽思,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片见擦。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡钉汗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鲤屡,到底是詐尸還是另有隱情损痰,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布酒来,位于F島的核電站卢未,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏堰汉。R本人自食惡果不足惜辽社,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望翘鸭。 院中可真熱鬧滴铅,春花似錦、人聲如沸矮固。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽档址。三九已至,卻和暖如春邻梆,著一層夾襖步出監(jiān)牢的瞬間守伸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工浦妄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留尼摹,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓剂娄,卻偏偏與公主長得像蠢涝,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子阅懦,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355