ReentrantLock實(shí)現(xiàn)原理深入探究

本文轉(zhuǎn)載自【五月的倉(cāng)頡】的博客 原文

本文知識(shí)點(diǎn)

  • 前言
  • 什么是AbstractQueuedSynchronizer?
  • ReentrantLock的實(shí)現(xiàn)原理
  • lock()實(shí)現(xiàn)原理
  • unlock()實(shí)現(xiàn)原理
  • ReentrantLock其他方法的實(shí)現(xiàn)
  • 小結(jié)

前言

??網(wǎng)上寫(xiě)ReentrantLock的使用、ReentrantLock和synchronized的區(qū)別的文章很多筒狠,研究ReentrantLock并且能講清楚ReentrantLock的原理的文章很少田柔,本文就來(lái)研究一下ReentrantLock的實(shí)現(xiàn)原理助析。研究ReentrantLock的實(shí)現(xiàn)原理需要比較好的Java基礎(chǔ)以及閱讀代碼的能力,有些朋友看不懂沒(méi)關(guān)系,可以以后看,相信你一定會(huì)有所收獲湘纵。
ReentrantLock是基于AQS實(shí)現(xiàn)的,這在下面會(huì)講到滤淳,AQS的基礎(chǔ)又是CAS梧喷,如果不是很熟悉CAS的朋友,可以看一下這篇文章Unsafe與CAS脖咐。

什么是AbstractQueuedSynchronizer?

??ReentrantLock實(shí)現(xiàn)的前提就是AbstractQueuedSynchronizer铺敌,簡(jiǎn)稱AQS,是java.util.concurrent的核心屁擅,CountDownLatch偿凭、FutureTask、Semaphore派歌、ReentrantLock等都有一個(gè)內(nèi)部類是這個(gè)抽象類的子類弯囊。先用兩張表格介紹一下AQS。第一個(gè)講的是Node胶果,由于AQS是基于FIFO隊(duì)列的實(shí)現(xiàn)匾嘱,因此必然存在一個(gè)個(gè)節(jié)點(diǎn),Node就是一個(gè)節(jié)點(diǎn)早抠。
Node里面的部分屬性

屬性 定義
Node SHARED = new Node() 表示Node處于共享模式
Node EXCLUSIVE = null 表示Node處于獨(dú)占模式
int CANCELLED = 1 因?yàn)槌瑫r(shí)或者中斷霎烙,Node被設(shè)置為取消狀態(tài),被取消的Node不應(yīng)該去競(jìng)爭(zhēng)鎖蕊连,只能保持取消狀態(tài)不變吼过,不能轉(zhuǎn)換為其他狀態(tài),處于這種狀態(tài)的Node會(huì)被踢出隊(duì)列咪奖,被GC回收
int SIGNAL = -1 表示這個(gè)Node的繼任Node被阻塞了,到時(shí)需要通知它
int CONDITION = -2 表示這個(gè)Node在條件隊(duì)列中酱床,因?yàn)榈却硞€(gè)條件而被阻塞
int PROPAGATE = -3 使用在共享模式頭Node有可能處于這種狀態(tài)羊赵, 表示鎖的下一次獲取可以無(wú)條件傳播
int waitStatus 0,新Node會(huì)處于這種狀態(tài)
Node prev 隊(duì)列中某個(gè)Node的前驅(qū)Node
Node next 隊(duì)列中某個(gè)Node的后繼Node
Thread thread 這個(gè)Node持有的線程扇谣,表示等待鎖的線程
Node nextWaiter 表示下一個(gè)等待condition的Node

看完了Node昧捷,下面再看一下AQS中有哪些變量和方法:

AQS里面的部分屬性

屬性/方法 含義
Thread exclusiveOwnerThread 這個(gè)是AQS父類AbstractOwnableSynchronizer的屬性,表示獨(dú)占模式同步器的當(dāng)前擁有者
Node 上面已經(jīng)介紹過(guò)了罐寨,F(xiàn)IFO隊(duì)列的基本單位
Node head FIFO隊(duì)列中的頭Node
Node tail FIFO隊(duì)列中的尾Node
int state 同步狀態(tài)靡挥,0表示未鎖
int getState() 獲取同步狀態(tài)
setState(int newState) 設(shè)置同步狀態(tài)
boolean compareAndSetState(int expect, int update) 利用CAS進(jìn)行State的設(shè)置
long spinForTimeoutThreshold = 1000L 線程自旋等待的時(shí)間
Node enq(final Node node) 插入一個(gè)Node到FIFO隊(duì)列中
Node addWaiter(Node mode) 為當(dāng)前線程和指定模式創(chuàng)建并擴(kuò)充一個(gè)等待隊(duì)列
void setHead(Node node) 設(shè)置隊(duì)列的頭Node
void unparkSuccessor(Node node) 如果存在的話,喚起Node持有的線程
void doReleaseShared() 共享模式下做釋放鎖的動(dòng)作
void cancelAcquire(Node node) 取消正在進(jìn)行的Node獲取鎖的嘗試
boolean shouldParkAfterFailedAcquire(Node pred, Node node) 在嘗試獲取鎖失敗后是否應(yīng)該禁用當(dāng)前線程并等待
void selfInterrupt() 中斷當(dāng)前線程本身
boolean parkAndCheckInterrupt() 禁用當(dāng)前線程進(jìn)入等待狀態(tài)并中斷線程本身
boolean acquireQueued(final Node node, int arg) 隊(duì)列中的線程獲取鎖
tryAcquire(int arg) 嘗試獲得鎖由AQS的子類實(shí)現(xiàn)它
tryRelease(int arg) 嘗試釋放鎖由AQS的子類實(shí)現(xiàn)它
isHeldExclusively() 是否獨(dú)自持有鎖
acquire(int arg) 獲取鎖
release(int arg) 釋放鎖
compareAndSetHead(Node update) 利用CAS設(shè)置頭Node
compareAndSetTail(Node expect, Node update) 利用CAS設(shè)置尾Node
compareAndSetWaitStatus(Node node, int expect, int update) 利用CAS設(shè)置某個(gè)Node中的等待狀態(tài)

??上面列出了AQS中最主要的一些方法和屬性鸯绿。<font color=red>整個(gè)AQS是典型的模板模式的應(yīng)用</font>跋破,設(shè)計(jì)得十分精巧簸淀,對(duì)于FIFO隊(duì)列的各種操作在AQS中已經(jīng)實(shí)現(xiàn)了,<font color=red>AQS的子類一般只需要重寫(xiě)tryAcquire(int arg)和tryRelease(int arg)兩個(gè)方法即可</font>毒返。

ReentrantLock實(shí)現(xiàn)原理(源碼較多)

ReentrantLock中有一個(gè)抽象類Sync:

    private final Sync sync;
    /**
    * Base of synchronization control for this lock. Subclassed
    * into fair and nonfair versions below. Uses AQS state to
    * represent the number of holds on the lock.
    */
   abstract static class Sync extends AbstractQueuedSynchronizer {
    ...
    }

??ReentrantLock根據(jù)傳入構(gòu)造方法的布爾型參數(shù)實(shí)例化出Sync的實(shí)現(xiàn)類FairSync和NonfairSync租幕,分別表示公平的Sync和非公平的Sync。由于ReentrantLock我們用的比較多的是非公平鎖拧簸,所以看下非公平鎖是如何實(shí)現(xiàn)的劲绪。假設(shè)線程1調(diào)用了ReentrantLock的lock()方法,那么線程1將會(huì)獨(dú)占鎖盆赤,整個(gè)調(diào)用鏈?zhǔn)趾?jiǎn)單:

image

第一個(gè)獲取鎖的線程就做了兩件事情:

  • 1贾富、設(shè)置AbstractQueuedSynchronizer的state為1
  • 2、設(shè)置AbstractOwnableSynchronizer的thread為當(dāng)前線程

??這兩步做完之后就表示線程1獨(dú)占了鎖牺六。然后線程2也要嘗試獲取同一個(gè)鎖颤枪,在線程1沒(méi)有釋放鎖的情況下必然是行不通的,所以線程2就要阻塞兔乞。那么汇鞭,線程2如何被阻塞?看下線程2的方法調(diào)用鏈庸追,這就比較復(fù)雜了:

image

調(diào)用鏈看到確實(shí)非常長(zhǎng)霍骄,沒(méi)關(guān)系,結(jié)合代碼分析一下淡溯,其實(shí)ReentrantLock沒(méi)有那么復(fù)雜读整,我們一點(diǎn)點(diǎn)來(lái)扒代碼:

lock()的實(shí)現(xiàn)原理

final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
       acquire(1);
}

??首先線程2嘗試?yán)肅AS去判斷state是不是0,是0就設(shè)置為1咱娶,當(dāng)然這一步操作肯定是失敗的米间,因?yàn)榫€程1已經(jīng)將state設(shè)置成了1,所以第2行必定是false膘侮,因此線程2走第5行的acquire方法:

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

??從字面上就很好理解這個(gè)if的意思屈糊,先走第一個(gè)判斷條件嘗試獲取一次鎖,如果獲取的結(jié)果為false即失敗琼了,走第二個(gè)判斷條件添加FIFO等待隊(duì)列逻锐。所以先看一下tryAcquire方法做了什么,這個(gè)方法最終調(diào)用到的是Sync的nonfairTryAcquire方法:

1.final boolean nonfairTryAcquire(int acquires) {
2.    final Thread current = Thread.currentThread();
3.    int c = getState();
4.    if (c == 0) {
5.        if (compareAndSetState(0, acquires)) {
6.            setExclusiveOwnerThread(current);
7.            return true;
8.        }
9.    }
10.    else if (current == getExclusiveOwnerThread()) {
11.        int nextc = c + acquires;
12.        if (nextc < 0) // overflow
13.            throw new Error("Maximum lock count exceeded");
14.        setState(nextc);
15.        return true;
16    }
17.    return false;
18.}

??由于state是volatile的雕薪,所以state對(duì)線程2具有可見(jiàn)性昧诱,線程2拿到最新的state,再次判斷一下能否持有鎖(可能線程1同步代碼執(zhí)行得比較快所袁,這會(huì)兒已經(jīng)釋放了鎖)盏档,不可以就返回false。

??注意一下第10~第16行燥爷,這段代碼的作用是讓某個(gè)線程可以多次調(diào)用同一個(gè)ReentrantLock蜈亩,每調(diào)用一次給state+1懦窘,由于某個(gè)線程已經(jīng)持有了鎖,所以這里不會(huì)有競(jìng)爭(zhēng)勺拣,因此不需要利用CAS設(shè)置state(相當(dāng)于一個(gè)偏向鎖)奶赠。從這段代碼可以看到,nextc每次加1药有,當(dāng)nextc<0的時(shí)候拋出error毅戈,那么同一個(gè)鎖最多能重入Integer.MAX_VALUE次,也就是2147483647愤惰。

??然后就走到if的第二個(gè)判斷里面了苇经,先走AQS的addWaiter方法:

private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    enq(node);
    return node;
}

??先創(chuàng)建一個(gè)當(dāng)前線程的Node,模式為獨(dú)占模式(因?yàn)閭魅氲膍ode是一個(gè)NULL)宦言,再判斷一下隊(duì)列上有沒(méi)有節(jié)點(diǎn)扇单,沒(méi)有就創(chuàng)建一個(gè)隊(duì)列,因此走enq方法:

1.private Node enq(final Node node) {
2.    for (;;) {
3.        Node t = tail;
4.        if (t == null) { // Must initialize
5.            if (compareAndSetHead(new Node()))
6.                tail = head;
7.        } else {
8.            node.prev = t;
9.            if (compareAndSetTail(t, node)) {
10.                t.next = node;
11.                return t;
12.            }
13.        }
14.    }
15.}

這個(gè)方法其實(shí)畫(huà)一張圖應(yīng)該比較好理解奠旺,形成一個(gè)隊(duì)列之后應(yīng)該是這樣的:

image

??每一步都用圖表示出來(lái)了蜘澜,由于線程2所在的Node是第一個(gè)要等待的Node,因此FIFO隊(duì)列上肯定沒(méi)有內(nèi)容响疚,tail為null鄙信,走的就是第4行~第6行的代碼邏輯。這里用了CAS設(shè)置頭Node忿晕,當(dāng)然有可能線程2設(shè)置頭Node的時(shí)候CPU切換了装诡,線程3已經(jīng)把頭Node設(shè)置好了形成了上圖所示的一個(gè)隊(duì)列,這時(shí)線程2再循環(huán)一次獲取tail践盼,由于tail是volatile的鸦采,所以對(duì)線程2可見(jiàn),線程2看見(jiàn)tail不為null咕幻,就走到了7行的else里面去往尾Node后面添加自身渔伯。整個(gè)過(guò)程下來(lái),形成了一個(gè)雙向隊(duì)列肄程。最后走AQS的acquireQueued(node, 1):

1.final boolean acquireQueued(final Node node, int arg) {
2.   boolean failed = true;
3.    try {
4.        boolean interrupted = false;
5.        for (;;) {
6.            final Node p = node.predecessor();
7.            if (p == head && tryAcquire(arg)) {
8.                setHead(node);
9.                p.next = null; // help GC
10.                failed = false;
11.                return interrupted;
12.            }
13.            if (shouldParkAfterFailedAcquire(p, node) &&
14.                parkAndCheckInterrupt())
15.                interrupted = true;
16.        }
17.    } finally {
18.        if (failed)
19.            cancelAcquire(node);
20.    }
21.}

??此時(shí)再做判斷锣吼,由于線程2是雙向隊(duì)列的真正的第一個(gè)Node(前面還有一個(gè)h),所以第5行~第11行再次判斷一下線程2能不能獲取鎖(可能這段時(shí)間內(nèi)線程1已經(jīng)執(zhí)行完了把鎖釋放了绷耍,state從1變?yōu)榱?),如果還是不行鲜侥,先調(diào)用AQS的shouldParkAfterFailedAcquire(p, node)方法:

1.private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
2.    int ws = pred.waitStatus;
3.    if (ws == Node.SIGNAL)
4.        /*
5.         * This node has already set status asking a release
6.         * to signal it, so it can safely park.
7.         */
9.        return true;
10.    if (ws > 0) {
11.        /*
12.         * Predecessor was cancelled. Skip over predecessors and
13.         * indicate retry.
14.         */
15.        do {
16.            node.prev = pred = pred.prev;
17.        } while (pred.waitStatus > 0);
18.        pred.next = node;
19.    } else {
20.        /*
21.         * waitStatus must be 0 or PROPAGATE.  Indicate that we
22.         * need a signal, but don't park yet.  Caller will need to
23.         * retry to make sure it cannot acquire before parking.
24.         */
25.        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
26.    }
27.    return false;
28.}

??這個(gè)waitStatus是h的waitStatus褂始,很明顯是0,所以此時(shí)把h的waitStatus設(shè)置為Noed.SIGNAL即-1并返回false描函。既然返回了false崎苗,上面的acquireQueued的13行if自然不成立狐粱,再走一次for循環(huán),還是先嘗試獲取鎖胆数,不成功肌蜻,繼續(xù)走shouldParkAfterFailedAcquire,此時(shí)waitStatus為-1必尼,走第3行的判斷蒋搜,返回true。然后走acquireQueued的13行if的第二個(gè)判斷條件parkAndCheckInterrupt:

private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}
public static void park(Object blocker) {
    Thread t = Thread.currentThread();
    setBlocker(t, blocker);
    UNSAFE.park(false, 0L);
    setBlocker(t, null);
}

??最后一步判莉,調(diào)用LockSupport(阻塞原語(yǔ))的park方法阻塞住了當(dāng)前的線程豆挽。至此,使用ReentrantLock讓線程1獨(dú)占鎖券盅、線程2進(jìn)入FIFO隊(duì)列并阻塞的完整流程已經(jīng)整理出來(lái)了帮哈。

lock()的操作明了之后,就要探究一下unlock()的時(shí)候代碼又做了什么了锰镀,接著看下一部分娘侍。

unlock的實(shí)現(xiàn)原理

就不畫(huà)流程圖了,直接看一下代碼流程泳炉,比較簡(jiǎn)單憾筏,調(diào)用ReentrantLock的unlock方法:

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

走AQS的release:

1.public final boolean release(int arg) {
2.    if (tryRelease(arg)) {
3.        Node h = head;
4.        if (h != null && h.waitStatus != 0)
5.            unparkSuccessor(h);
6.        return true;
7.    }
8.    return false;
9.}

先調(diào)用Sync的tryRelease嘗試釋放鎖:

protected final boolean tryRelease(int releases) {
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }

??首先,只有當(dāng)c==0的時(shí)候才會(huì)讓free=true胡桃,這和上面一個(gè)線程多次調(diào)用lock方法累加state是對(duì)應(yīng)的踩叭,調(diào)用了多少次的lock()方法自然必須調(diào)用同樣次數(shù)的unlock()方法才行,這樣才把一個(gè)鎖給全部解開(kāi)翠胰。

??當(dāng)一條線程對(duì)同一個(gè)ReentrantLock全部解鎖之后容贝,AQS的state自然就是0了,AbstractOwnableSynchronizer的exclusiveOwnerThread將被設(shè)置為null之景,這樣就表示沒(méi)有線程占有鎖斤富,方法返回true。代碼繼續(xù)往下走锻狗,上面的release方法的第4行满力,h不為null成立,h的waitStatus為-1轻纪,不等于0也成立油额,所以走第5行的unparkSuccessor方法:

1.private void unparkSuccessor(Node node) {
2.    int ws = node.waitStatus;
3.    if (ws < 0)
4.        compareAndSetWaitStatus(node, ws, 0);
5.    Node s = node.next;
6.    if (s == null || s.waitStatus > 0) {
7.        s = null;
8.        for (Node t = tail; t != null && t != node; t = t.prev)
9.            if (t.waitStatus <= 0)
10.                s = t;
11.    }
12.    if (s != null)
13.        LockSupport.unpark(s.thread);
14.}

??s即h的下一個(gè)Node,這個(gè)Node里面的線程就是線程2刻帚,由于這個(gè)Node不等于null潦嘶,所以走12行,線程2被unPark了崇众,得以運(yùn)行掂僵。有一個(gè)很重要的問(wèn)題是:鎖被解了怎樣保證整個(gè)FIFO隊(duì)列減少一個(gè)Node呢航厚?這是一個(gè)很巧妙的設(shè)計(jì),又回到了AQS的acquireQueued方法了:

1.final boolean acquireQueued(final Node node, int arg) {
2.   boolean failed = true;
3.    try {
4.        boolean interrupted = false;
5.        for (;;) {
6.            final Node p = node.predecessor();
7.            if (p == head && tryAcquire(arg)) {
8.                setHead(node);
9.                p.next = null; // help GC
10.                failed = false;
11.                return interrupted;
12.            }
13.            if (shouldParkAfterFailedAcquire(p, node) &&
14.                parkAndCheckInterrupt())
15.                interrupted = true;
16.        }
17.    } finally {
18.        if (failed)
19.            cancelAcquire(node);
20.    }
21.}

??<font color=red>被阻塞的線程2是被阻塞在第14行锰蓬,注意這里并沒(méi)有return語(yǔ)句幔睬,也就是說(shuō),阻塞完成線程2依然會(huì)進(jìn)行for循環(huán)芹扭。</font>然后麻顶,阻塞完成了,線程2所在的Node的前驅(qū)Node是p冯勉,線程2嘗試tryAcquire澈蚌,成功,然后線程2就成為了head節(jié)點(diǎn)了灼狰,把p的next設(shè)置為null宛瞄,這樣原頭Node里面的所有對(duì)象都不指向任何塊內(nèi)存空間,h屬于棧內(nèi)存的內(nèi)容交胚,方法結(jié)束被自動(dòng)回收份汗,這樣隨著方法的調(diào)用完畢,原頭Node也沒(méi)有任何的引用指向它了蝴簇,這樣它就被GC自動(dòng)回收了杯活。此時(shí),遇到一個(gè)return語(yǔ)句熬词,acquireQueued方法結(jié)束旁钧,后面的Node也是一樣的原理。

這里有一個(gè)細(xì)節(jié)互拾,看一下setHead方法:

private void setHead(Node node) {
    head = node;
    node.thread = null;
    node.prev = null;
}

??setHead方法里面的前驅(qū)Node是Null歪今,也沒(méi)有線程,那么為什么不用一個(gè)在等待的線程作為Head Node呢颜矿?

??因?yàn)橐粋€(gè)線程隨時(shí)有可能因?yàn)橹袛喽∠男桑∠脑挘琋ode自然就要被GC了骑疆,那GC前必然要把頭Node的后繼Node變?yōu)橐粋€(gè)新的頭而且要應(yīng)對(duì)多種情況田篇,這樣就很麻煩。用一個(gè)沒(méi)有thread的Node作為頭箍铭,相當(dāng)于起了一個(gè)引導(dǎo)作用泊柬,因?yàn)閔ead沒(méi)有線程,自然也不會(huì)被取消诈火。

??再看一下上面unparkSuccessor的5行~10行兽赁,就是為了防止head的下一個(gè)node被取消的情況,這樣,就從尾到頭遍歷闸氮,找出離head最近的一個(gè)node,對(duì)這個(gè)node進(jìn)行unPark操作教沾。

ReentrantLock其他方法的實(shí)現(xiàn)

??如果能理解ReentrantLock的實(shí)現(xiàn)方式蒲跨,那么你會(huì)發(fā)現(xiàn)ReentrantLock中其余一些方法的實(shí)現(xiàn)還是很簡(jiǎn)單的,從JDK API關(guān)于ReentrantLock方法的介紹這部分授翻,舉幾個(gè)例子:

  • 1或悲、int getHoldCount()
final int getHoldCount() {
    return isHeldExclusively() ? getState() : 0;
}

獲取ReentrantLock的lock()方法被調(diào)用了幾次,就是state的當(dāng)前值

  • 2堪唐、Thread getOwner()
final Thread getOwner() {
    return getState() == 0 ? null : getExclusiveOwnerThread();
}

獲取當(dāng)前占有鎖的線程巡语,就是AbstractOwnableSynchronizer中exclusiveOwnerThread的值

  • 3、Collection<Thread> getQueuedThreads()
public final Collection<Thread> getQueuedThreads() {
    ArrayList<Thread> list = new ArrayList<Thread>();
    for (Node p = tail; p != null; p = p.prev) {
        Thread t = p.thread;
        if (t != null)
            list.add(t);
    }
    return list;
}

從尾到頭遍歷一下淮菠,添加進(jìn)ArrayList中

  • 4男公、int getQueuedLength()
public final int getQueueLength() {
    int n = 0;
    for (Node p = tail; p != null; p = p.prev) {
        if (p.thread != null)
            ++n;
    }
    return n;
}

??從尾到頭遍歷一下,累加n合陵。當(dāng)然這個(gè)方法和上面那個(gè)方法可能是不準(zhǔn)確的枢赔,因?yàn)楸闅v的時(shí)候可能別的線程又往隊(duì)列尾部添加了Node。

??其余方法也都差不多拥知,可以自己去看一下踏拜。


小結(jié)

??本文從源碼分析了ReentrantLock的非公平加鎖和釋放鎖的原理,通過(guò)源碼我們得知ReentrantLock的基石是AQS低剔,AQS的基石是Node速梗,多線程競(jìng)爭(zhēng)鎖時(shí)通過(guò)自旋方式等待資源,通過(guò)Unsafe提供的compareAndSwapObject方法達(dá)到多線程競(jìng)爭(zhēng)鎖時(shí)只有一個(gè)線程可以獲取鎖的效果襟齿,從而實(shí)現(xiàn)了線程安全姻锁。
??翻看了一下公平加鎖的源碼,每次在獲取鎖時(shí)都去檢查FIFO隊(duì)列是否已經(jīng)有等待線程蕊唐,如果有就把自己追加到FIFO隊(duì)列的末尾屋摔,而非公平加鎖則是每次都先去嘗試獲取鎖,獲取不到再加入到FIFO隊(duì)列替梨。
??另外本文代碼并非照搬原博主的代碼钓试,而是在jdk8的源碼基礎(chǔ)上復(fù)制過(guò)來(lái)的,與原博主貼的代碼有稍許偏差副瀑,不過(guò)核心實(shí)現(xiàn)是一致的弓熏。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市糠睡,隨后出現(xiàn)的幾起案子挽鞠,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件信认,死亡現(xiàn)場(chǎng)離奇詭異材义,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)嫁赏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)其掂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人潦蝇,你說(shuō)我怎么就攤上這事款熬。” “怎么了攘乒?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵贤牛,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我则酝,道長(zhǎng)殉簸,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任沽讹,我火速辦了婚禮喂链,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘妥泉。我一直安慰自己椭微,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布盲链。 她就那樣靜靜地躺著蝇率,像睡著了一般。 火紅的嫁衣襯著肌膚如雪刽沾。 梳的紋絲不亂的頭發(fā)上本慕,一...
    開(kāi)封第一講書(shū)人閱讀 51,562評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音侧漓,去河邊找鬼锅尘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛布蔗,可吹牛的內(nèi)容都是我干的藤违。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼纵揍,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼顿乒!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起泽谨,我...
    開(kāi)封第一講書(shū)人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤璧榄,失蹤者是張志新(化名)和其女友劉穎特漩,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體骨杂,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涂身,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了搓蚪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片访得。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖陕凹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鳄炉,我是刑警寧澤杜耙,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站拂盯,受9級(jí)特大地震影響佑女,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜谈竿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一团驱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧空凸,春花似錦嚎花、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至道逗,卻和暖如春兵罢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背滓窍。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工卖词, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吏夯。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓此蜈,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親噪生。 傳聞我的和親對(duì)象是個(gè)殘疾皇子舶替,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355