ReentrantLock源碼解析

首先來(lái)看ReentrantLock的公平鎖實(shí)現(xiàn)源碼

    Lock lock = new ReentrantLock(true);
    lock.lock();
    
    public void lock() {
        sync.lock();
    }
    
    /**
    *  lock方法調(diào)用acquire
    **/
    final void lock() {
        acquire(1);
    }

   /**
    *  acquire方法實(shí)現(xiàn)
    **/
    public final void acquire(int arg) {
        //tryAcquire(arg)嘗試加鎖票罐,如果加鎖失敗則會(huì)調(diào)用acquireQueued方法加入隊(duì)列去排隊(duì)禀倔,如果加鎖成功則不會(huì)調(diào)用
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

\color{red}{公平鎖首先會(huì)調(diào)用tryAcquire去嘗試加鎖,當(dāng)然這里的嘗試加鎖并不是直接加鎖}

  • 第一步便是判斷鎖是不是自由狀態(tài)扭屁,如果是則判斷直接是否需要排隊(duì)(hasQueuedPredecessors方法判斷隊(duì)列是否被初始化(如果沒(méi)有初始化顯然不需要排隊(duì),隊(duì)列如果被初始化了痹栖,則自己有可能需要排隊(duì)));如果hasQueuedPredecessors返回false潘酗,由于取反了故而不需要排隊(duì)則進(jìn)行Cas操作去上鎖杆兵,如果需要排隊(duì)則不會(huì)進(jìn)入if分支當(dāng)中,會(huì)直接返回false表示加鎖失敗
  • 第二步如果不是自由狀態(tài)再判斷是不是重入仔夺,如果不是重入則直接返回false加鎖失敗琐脏,如果是重入則把計(jì)數(shù)器+1

下面來(lái)一步步看源碼

鎖對(duì)象:其實(shí)就是ReentrantLock的實(shí)例對(duì)象,上述代碼第一行中的lock對(duì)象就是所謂的鎖
自由狀態(tài):自由狀態(tài)表示鎖對(duì)象沒(méi)有被別的線程持有缸兔,計(jì)數(shù)器為0
計(jì)數(shù)器:再lock對(duì)象中有一個(gè)字段state用來(lái)記錄上鎖次數(shù)日裙,比如lock對(duì)象是自由狀態(tài)則state為0,如果大于零則表示被線程持有了惰蜜,當(dāng)然也有重入那么state則>1
waitStatus:僅僅是一個(gè)狀態(tài)而已昂拂;ws是一個(gè)過(guò)渡狀態(tài),在不同方法里面判斷ws的狀態(tài)做不同的處理抛猖,所以ws=0有其存在的必要性
tail:隊(duì)列的隊(duì)尾
head:隊(duì)列的對(duì)首
ts:第二個(gè)給lock加鎖的線程
tf:第一個(gè)給lock加鎖的線程
tc:當(dāng)前給線程加鎖的線程
tl:最后一個(gè)加鎖的線程
tn:隨便某個(gè)線程 當(dāng)然這些線程有可能重復(fù)格侯,比如第一次加鎖的時(shí)候tf==tc==tl==tn
節(jié)點(diǎn):就是上面的Node類的對(duì)象,里面封裝了線程樟结,所以某種意義上node就等于一個(gè)線程

tryAcquire(1)

        protected final boolean tryAcquire(int acquires) {
            //獲取當(dāng)前線程
            final Thread current = Thread.currentThread();
            //獲取lock對(duì)象的上鎖狀態(tài),如果鎖是自由狀態(tài)則=0精算,如果被上鎖則為1瓢宦,大于1表示重入
            int c = getState();
            if (c == 0) { //當(dāng)前鎖是自由狀態(tài),沒(méi)有人占用
                //hasQueuedPredecessors灰羽,判斷自己是否需要排隊(duì)驮履,不需要排隊(duì)則返回false,取反則是true
                //不需要排隊(duì)則進(jìn)行cas嘗試枷鎖,如果加鎖成功則把當(dāng)前線程設(shè)置為擁有鎖的線程
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果C不等于0廉嚼,而且當(dāng)前線程不等于擁有鎖的線程則不會(huì)進(jìn)else if 直接返回false玫镐,加鎖失敗
            //如果C不等于0,但是當(dāng)前線程等于擁有鎖的線程則表示這是一次重入怠噪,那么直接把狀態(tài)+1表示重入次數(shù)+1
            //那么這里也側(cè)面說(shuō)明了reentrantlock是可以重入的恐似,因?yàn)槿绻侵厝胍卜祷豻rue,也能lock成功
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

    protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }

下面看hasQueuedPredecessors()方法

    public final boolean hasQueuedPredecessors() {
        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());
    }
排隊(duì)結(jié)構(gòu)圖.png

可以想到有以下這么幾種情況

第一種情況

隊(duì)列沒(méi)有初始化傍念,不需要排隊(duì)矫夷,直接去cas加鎖,但是可能會(huì)失敗
假設(shè)兩個(gè)線程同時(shí)來(lái)lock憋槐,都看到隊(duì)列沒(méi)有初始化双藕,都認(rèn)為不需要排隊(duì),都去進(jìn)行CAS修改計(jì)數(shù)器阳仔;但是某個(gè)線程t1先拿到鎖忧陪,那么另外一個(gè)t2則會(huì)CAS失敗,這個(gè)時(shí)候他就會(huì)初始化隊(duì)列,并排隊(duì)

第二種情況

隊(duì)列被初始化了嘶摊,但是thread過(guò)來(lái)加鎖延蟹,發(fā)覺(jué)隊(duì)列當(dāng)中第一個(gè)排隊(duì)的就是自己(比如重入)這個(gè)時(shí)候他也有可能不需要排隊(duì);為什么不需要排隊(duì)更卒?
因?yàn)殛?duì)列當(dāng)中第一個(gè)排隊(duì)的線程他會(huì)去嘗試獲取一下鎖等孵,因?yàn)橛锌赡苓@個(gè)時(shí)候持有鎖鎖的那個(gè)線程可能釋放了鎖,如果釋放了就直接獲取鎖執(zhí)行蹂空。但是如果沒(méi)有釋放他就會(huì)去排隊(duì)俯萌。

  • h != t 判斷首不等于尾這里要分三種情況

1、隊(duì)列沒(méi)有初始化上枕,也就是第一個(gè)線程thread來(lái)加鎖的時(shí)候那么這個(gè)時(shí)候隊(duì)列沒(méi)有初始化咐熙,h和t都是null,那么這個(gè)時(shí)候不等于不成立(false)辨萍,直接返回棋恼。

2、隊(duì)列被初始化了锈玉,后面我們會(huì)分析隊(duì)列初始化的流程爪飘,如果隊(duì)列被初始化那么h!=t則成立(假設(shè)隊(duì)列里的數(shù)據(jù)大于1);h != t 返回true拉背。
Node h = head; h.next賦值給s师崎;s表示頭的下一個(gè),則表示他是隊(duì)列當(dāng)中參與排隊(duì)的線程而且是排在最前面的椅棺;為什么是s最前面不是h嘛犁罩?誠(chéng)然h是隊(duì)列里面的第一個(gè),但是不是排隊(duì)的第一個(gè)两疚;因?yàn)閔是持有鎖的床估,但是不參與排隊(duì)(比如買火車票,第一個(gè)人已經(jīng)在買票了诱渤,他后面才算排隊(duì))

因?yàn)閔要么是虛擬出來(lái)的節(jié)點(diǎn)丐巫,要么是持有鎖的節(jié)點(diǎn)
初始化的時(shí)候,會(huì)虛擬一個(gè)空的節(jié)點(diǎn)

然后判斷s是否等于空勺美,其實(shí)就是判斷隊(duì)列里面是否只有一個(gè)數(shù)據(jù)鞋吉;假設(shè)隊(duì)列大于1個(gè),那么肯定不成立(s==null---->false)励烦,因?yàn)榇笥谝粋€(gè)h.next肯定不為空谓着;
由于是||運(yùn)算如果返回false,還要判斷s.thread != Thread.currentThread()坛掠;這里又分為兩種情況

  • 2.1 s.thread != Thread.currentThread() 返回true赊锚,就是當(dāng)前線程不等于在排隊(duì)的第一個(gè)線程s治筒;
    方法最終返回true,那么則需要去排隊(duì)舷蒲。隊(duì)列不為空耸袜,有人在排隊(duì),而且第一個(gè)排隊(duì)的人和現(xiàn)在來(lái)參與競(jìng)爭(zhēng)的人不是同一個(gè)牲平,那么你就乖乖去排隊(duì)

  • 2.2 s.thread != Thread.currentThread() 返回false 表示當(dāng)前來(lái)參與競(jìng)爭(zhēng)鎖的線程和第一個(gè)排隊(duì)的線程是同一個(gè)線程
    方法最終返回false堤框,那么去則不需要去排隊(duì)
    不需要排隊(duì)則調(diào)用 compareAndSetState(0, acquires)去改變計(jì)數(shù)器嘗試上鎖;這里又分為兩種情況

    • 2.2.1 第一種情況加鎖成功
      假如這個(gè)時(shí)候h也就是持有鎖的那個(gè)線程執(zhí)行完了纵柿,釋放鎖了蜈抓,那么加鎖成功;成功則執(zhí)行 setExclusiveOwnerThread(current); 然后返回true
    • 2.2.2 第二種情況加鎖失敗
      假如這個(gè)時(shí)候h也就是持有鎖的那個(gè)線程沒(méi)執(zhí)行完昂儒,沒(méi)釋放鎖沟使,那么加鎖失敗
總結(jié)第二種情況

如果隊(duì)列被初始化了,而且至少有一個(gè)人在排隊(duì)那么自己也去排隊(duì)渊跋;但是有個(gè)插曲腊嗡;他會(huì)去看看那個(gè)第一個(gè)排隊(duì)的人是不是自己,如果是自己那么他就去嘗試加鎖拾酝;嘗試看看鎖有沒(méi)有釋放

第三種情況

隊(duì)列被初始化了燕少,但是里面只有一個(gè)數(shù)據(jù);什么情況下才會(huì)出現(xiàn)這種情況呢蒿囤?
注意不是ts加鎖的時(shí)候里面就只有一個(gè)數(shù)據(jù)客们,因?yàn)殛?duì)列初始化的時(shí)候會(huì)虛擬一個(gè)h作為頭結(jié)點(diǎn),當(dāng)前線程作為第一個(gè)排隊(duì)的節(jié)點(diǎn)蟋软。
因?yàn)閍qs認(rèn)為h永遠(yuǎn)是不排隊(duì)的镶摘,假設(shè)你不虛擬節(jié)點(diǎn)出來(lái)那么ts就是h嗽桩,而ts其實(shí)需要排隊(duì)的岳守,因?yàn)檫@個(gè)時(shí)候tf可能沒(méi)有執(zhí)行完,ts得不到鎖碌冶,故而他需要排隊(duì)湿痢;
假設(shè)原先隊(duì)列里面有5個(gè)人在排隊(duì),當(dāng)前面4個(gè)都執(zhí)行完了扑庞,輪到第五個(gè)線程得到鎖的時(shí)候譬重;他會(huì)把自己設(shè)置成為頭部,而尾部又沒(méi)有罐氨,故而隊(duì)列當(dāng)中只有一個(gè)h就是第五個(gè)臀规,因?yàn)檫@個(gè)時(shí)候第五個(gè)線程已經(jīng)不排隊(duì)了,他拿到鎖了栅隐,所以他不參與排隊(duì)塔嬉,故而需要設(shè)置成為h玩徊;即頭部;所以這個(gè)時(shí)間內(nèi)谨究,隊(duì)列當(dāng)中只有一個(gè)節(jié)點(diǎn)恩袱,這個(gè)時(shí)候隊(duì)列已經(jīng)初始化了,但是只有一個(gè)數(shù)據(jù)胶哲,并且這個(gè)數(shù)據(jù)所代表的線程是持有鎖
h != t false返回false

第三種情況總結(jié):

如果隊(duì)列當(dāng)中只有一個(gè)節(jié)點(diǎn)畔塔,而這種情況我們分析了,這個(gè)節(jié)點(diǎn)就是當(dāng)前持有鎖的那個(gè)節(jié)點(diǎn)鸯屿,故而我不需要排隊(duì)澈吨,進(jìn)行cas;
如果持有鎖的線程釋放了鎖碾盟,那么我能成功上鎖

加鎖過(guò)程總結(jié)

如果是第一個(gè)線程tf棚辽,那么和隊(duì)列無(wú)關(guān),線程直接持有鎖冰肴。并且也不會(huì)初始化隊(duì)列屈藐,如果接下來(lái)的線程都是交替執(zhí)行,那么永遠(yuǎn)和AQS隊(duì)列無(wú)關(guān)熙尉,都是直接線程持有鎖联逻,如果發(fā)生了競(jìng)爭(zhēng),比如tf持有鎖的過(guò)程中T2來(lái)lock检痰,那么這個(gè)時(shí)候就會(huì)初始化AQS包归,初始化AQS的時(shí)候會(huì)在隊(duì)列的頭部虛擬一個(gè)Thread為NULL的Node,因?yàn)殛?duì)列當(dāng)中的head永遠(yuǎn)是持有鎖的那個(gè)node(除了第一次會(huì)虛擬一個(gè)铅歼,其他時(shí)候都是持有鎖的那個(gè)線程鎖封裝的node)公壤,現(xiàn)在第一次的時(shí)候持有鎖的是tf而tf不在隊(duì)列當(dāng)中所以虛擬了一個(gè)node節(jié)點(diǎn),隊(duì)列當(dāng)中的除了head之外的所有的node都在park椎椰,當(dāng)tf釋放鎖之后unpark某個(gè)(基本是隊(duì)列當(dāng)中的第二個(gè)厦幅,為什么是第二個(gè)呢?前面說(shuō)過(guò)head永遠(yuǎn)是持有鎖的那個(gè)node慨飘,當(dāng)有時(shí)候也不會(huì)是第二個(gè)确憨,比如第二個(gè)被cancel之后,至于為什么會(huì)被cancel瓤的,不在我們討論范圍之內(nèi)休弃,cancel的條件很苛刻,基本不會(huì)發(fā)生)node之后圈膏,node被喚醒塔猾,假設(shè)node是t2,那么這個(gè)時(shí)候會(huì)首先把t2變成head(sethead)稽坤,在sethead方法里面會(huì)把t2代表的node設(shè)置為head丈甸,并且把node的Thread設(shè)置為null医增,為什么需要設(shè)置null?其實(shí)原因很簡(jiǎn)單老虫,現(xiàn)在t2已經(jīng)拿到鎖了叶骨,node就不要排隊(duì)了,那么node對(duì)Thread的引用就沒(méi)有意義了祈匙。所以隊(duì)列的head里面的Thread永遠(yuǎn)為null忽刽。

我們?cè)賮?lái)看看acquire方法方法
    public final void acquire(int arg) {
        //tryAcquire(arg)嘗試加鎖,如果加鎖失敗則會(huì)調(diào)用acquireQueued方法加入隊(duì)列去排隊(duì)夺欲,如果加鎖成功則不會(huì)調(diào)用
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

下面解析下acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法
如果代碼能執(zhí)行到這里跪帝,說(shuō)明當(dāng)前線程tc需要排隊(duì)

  • 需要排隊(duì)有兩種情況---換言之代碼能夠執(zhí)行到這里有兩種情況:
    1、tf持有了鎖些阅,并沒(méi)有釋放伞剑,所以tc來(lái)加鎖的時(shí)候需要排隊(duì),但這個(gè)時(shí)候隊(duì)列并沒(méi)有初始化
    2市埋、tn(無(wú)所謂哪個(gè)線程黎泣,反正就是一個(gè)線程)持有了鎖,那么由于加鎖tn!=tf(tf是屬于第一種情況缤谎,我們現(xiàn)在不考慮tf了)抒倚,所以隊(duì)列是一定被初始化了的,tc來(lái)加鎖坷澡,那么隊(duì)列當(dāng)中有人在排隊(duì)托呕,故而他也去排隊(duì)

先看addWaiter(Node.EXCLUSIVE)方法

//node的結(jié)構(gòu)
public class Node{
    volatile Node prev;
    volatile Node next;
    volatile Thread thread;
}

Node.EXCLUSIVE = null;

    private Node addWaiter(Node mode) {
    //由于AQS隊(duì)列當(dāng)中的元素類型為Node,故而需要把當(dāng)前線程tc封裝成為一個(gè)Node對(duì)象,下文我們叫做nc
    Node node = new Node(Thread.currentThread(), mode);
    //tail為對(duì)尾频敛,賦值給pred 
    Node pred = tail;
    //判斷pred是否為空项郊,其實(shí)就是判斷對(duì)尾是否有節(jié)點(diǎn),其實(shí)只要隊(duì)列被初始化了隊(duì)尾肯定不為空斟赚,假設(shè)隊(duì)列里面只有一個(gè)元素着降,那么對(duì)尾和對(duì)首都是這個(gè)元素
    //換言之就是判斷隊(duì)列有沒(méi)有初始化
    //上面我們說(shuō)過(guò)代碼執(zhí)行到這里有兩種情況,1汁展、隊(duì)列沒(méi)有初始化和2鹊碍、隊(duì)列已經(jīng)初始化了
    //pred不等于空表示第二種情況厌殉,隊(duì)列被初始化了食绿,如果是第二種情況那比較簡(jiǎn)單
   //直接把當(dāng)前線程封裝的nc的上一個(gè)節(jié)點(diǎn)設(shè)置成為pred即原來(lái)的對(duì)尾
   //繼而把pred的下一個(gè)節(jié)點(diǎn)設(shè)置為當(dāng)nc,這個(gè)nc自己成為對(duì)尾了
    if (pred != null) {
        //直接把當(dāng)前線程封裝的nc的上一個(gè)節(jié)點(diǎn)設(shè)置成為pred即原來(lái)的對(duì)尾公罕,對(duì)應(yīng) 10行的注釋
        node.prev = pred;
        //這里需要cas器紧,因?yàn)榉乐苟鄠€(gè)線程加鎖,確保nc入隊(duì)的時(shí)候是原子操作
        if (compareAndSetTail(pred, node)) {
            //繼而把pred的下一個(gè)節(jié)點(diǎn)設(shè)置為當(dāng)nc楼眷,這個(gè)nc自己成為對(duì)尾了 對(duì)應(yīng)第11行注釋
            pred.next = node;
            //然后把nc返回出去
            return node;
        }
    }
    //如果上面的if不成了就會(huì)執(zhí)行到這里铲汪,表示第一種情況隊(duì)列并沒(méi)有初始化---下面32行解析這個(gè)方法
    enq(node);
    //返回nc
    return node;
}



private Node enq(final Node node) {//這里的node就是當(dāng)前線程封裝的node也就是nc
    //死循環(huán)
    for (;;) {
        //對(duì)尾復(fù)制給t熊尉,上面已經(jīng)說(shuō)過(guò)隊(duì)列沒(méi)有初始化,故而第一次循環(huán)t==null(因?yàn)槭撬姥h(huán)掌腰,因此強(qiáng)調(diào)第一次狰住,后面可能還有第二次、第三次齿梁,每次t的情況肯定不同)
        Node t = tail;
        //第一次循環(huán)成了成立
        if (t == null) { // Must initialize
            //new Node就是實(shí)例化一個(gè)Node對(duì)象下文我們稱為nn催植,調(diào)用無(wú)參構(gòu)造方法實(shí)例化出來(lái)的Node里面三個(gè)屬性都為null
            //入隊(duì)操作--compareAndSetHead繼而把這個(gè)nn設(shè)置成為隊(duì)列當(dāng)中的頭部,cas防止多線程勺择、確保原子操作创南;記住這個(gè)時(shí)候隊(duì)列當(dāng)中只有一個(gè),即nn
            if (compareAndSetHead(new Node()))
                //這個(gè)時(shí)候AQS隊(duì)列當(dāng)中只有一個(gè)元素省核,即頭部=nn稿辙,所以為了確保隊(duì)列的完整,設(shè)置頭部等于尾部气忠,即nn即是頭也是尾
                //然后第一次循環(huán)結(jié)束
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

//為了方便 第二次循環(huán)我再貼一次代碼來(lái)對(duì)第二遍循環(huán)解釋
private Node enq(final Node node) {//這里的node就是當(dāng)前線程封裝的node也就是nc
    //死循環(huán)
    for (;;) {
        //對(duì)尾復(fù)制給t邻储,由于第二次循環(huán),故而tail==nn旧噪,即new出來(lái)的那個(gè)node
        Node t = tail;
        //第二次循環(huán)不成立
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            //不成立故而進(jìn)入else
            //首先把nc芥备,當(dāng)前線程所代表的的node的上一個(gè)節(jié)點(diǎn)改變?yōu)閚n,因?yàn)檫@個(gè)時(shí)候nc需要入隊(duì)舌菜,入隊(duì)的時(shí)候需要把關(guān)系維護(hù)好
            //所謂的維護(hù)關(guān)系就是形成鏈表萌壳,nc的上一個(gè)節(jié)點(diǎn)只能為nn,這個(gè)很好理解
            node.prev = t;
            //入隊(duì)操作--把nc設(shè)置為對(duì)尾日月,對(duì)首是nn袱瓮,
            if (compareAndSetTail(t, node)) {
                //上面我們說(shuō)了為了維護(hù)關(guān)系把nc的上一個(gè)節(jié)點(diǎn)設(shè)置為nn
                //這里同樣為了維護(hù)關(guān)系,把nn的下一個(gè)節(jié)點(diǎn)設(shè)置為nc
                t.next = node;
                //然后返回t爱咬,即nn尺借,死循環(huán)結(jié)束,也就是24行放回了精拟,這個(gè)返回其實(shí)就是為了終止循環(huán)燎斩,返回出去的t,沒(méi)有意義蜂绎,你可以查看24行代碼栅表。
                return t;
            }
        }
    }
}

  //這個(gè)方法已經(jīng)解釋完成了
  enq(node);
  //返回nc,不管哪種情況都會(huì)返回nc师枣;到此addWaiter方法解釋完成
  return node;


總結(jié)

addWaiter方法就是讓nc入隊(duì)-并且維護(hù)隊(duì)列的鏈表關(guān)系怪瓶,但是由于情況復(fù)雜做了不同處理
主要針對(duì)隊(duì)列是否有初始化,沒(méi)有初始化則new一個(gè)新的Node nn作為對(duì)首践美,nn里面的線程為null

acquireQueued(addWaiter(Node.exclusive),arg))經(jīng)過(guò)上面的解析之后可以理解成為
acquireQueued(nc,arg))

final boolean acquireQueued(final Node node, int arg) {//這里的node 就是當(dāng)前線程封裝的那個(gè)node 下文叫做nc
    //記住標(biāo)志很重要
    boolean failed = true;
    try {
        //同樣是一個(gè)標(biāo)志
        boolean interrupted = false;
        //死循環(huán)
        for (;;) {
            //獲取nc的上一個(gè)節(jié)點(diǎn)洗贰,有兩種情況找岖;1、上一個(gè)節(jié)點(diǎn)為頭部敛滋;2上一個(gè)節(jié)點(diǎn)不為頭部
            final Node p = node.predecessor();
            //如果nc的上一個(gè)節(jié)點(diǎn)為頭部许布,則表示nc為隊(duì)列當(dāng)中的第二個(gè)元素,為隊(duì)列當(dāng)中的第一個(gè)排隊(duì)人绎晃;
            //如果nc為隊(duì)列當(dāng)中的第二個(gè)元素爹脾,第一個(gè)排隊(duì)的則調(diào)用tryAcquire去嘗試假設(shè)
            //只有nc為第二個(gè)元素;第一個(gè)排隊(duì)的情況下才會(huì)嘗試加鎖箕昭,其他情況直接去park了灵妨,因?yàn)榈谝粋€(gè)排隊(duì)的執(zhí)行到這里的時(shí)候需要看看持有有鎖的線程有沒(méi)有釋放鎖,釋放了就輪到我了落竹,就不park了
            //有人會(huì)疑惑說(shuō)開(kāi)始調(diào)用tryAcquire加鎖失敗了(需要排隊(duì))泌霍,這里為什么還要進(jìn)行tryAcquire不是重復(fù)了嗎?
            //其實(shí)不然述召,因?yàn)榈谝淮蝨ryAcquire判斷是否需要排隊(duì)朱转,如果需要排隊(duì),那么我就入隊(duì)积暖;當(dāng)我入隊(duì)之后我發(fā)覺(jué)前面那個(gè)人就是第一個(gè)藤为,那么我不死心,再次問(wèn)問(wèn)前面那個(gè)人搞完沒(méi)有
            //如果搞完了夺刑,我就不park缅疟,接著他搞;如果他沒(méi)有搞完遍愿,那么我則在隊(duì)列當(dāng)中去park存淫,等待別人叫我
            //但是如果我去排隊(duì),發(fā)覺(jué)前面那個(gè)人在睡覺(jué)沼填,前面那個(gè)人都在睡覺(jué)桅咆,那么我也睡覺(jué)把
            if (p == head && tryAcquire(arg)) {
                //能夠執(zhí)行到這里表示我來(lái)加鎖的時(shí)候,鎖被持有了坞笙,我去排隊(duì)岩饼,進(jìn)到隊(duì)列當(dāng)中的時(shí)候發(fā)覺(jué)我前面那個(gè)人沒(méi)有park,前面那個(gè)人就是當(dāng)前持有鎖的那個(gè)人薛夜,那么我問(wèn)問(wèn)他搞完沒(méi)有
                //能夠進(jìn)到這個(gè)里面就表示前面那個(gè)人搞完了籍茧;所以這里能執(zhí)行到的幾率比較小却邓;但是在高并發(fā)的世界中這種情況真的需要考慮
                //如果我前面那個(gè)人搞完了硕糊,我nc得到鎖了院水,那么前面那個(gè)人直接出隊(duì)列腊徙,我自己則是對(duì)首简十;這行代碼就是設(shè)置自己為對(duì)首
                setHead(node);
                //這里的P代表的就是剛剛搞完事的那個(gè)人,由于他的事情搞完了撬腾,要出隊(duì)螟蝙;怎么出隊(duì)?把鏈表關(guān)系刪除
                p.next = null; // help GC
                //設(shè)置表示---記住記加鎖成功的時(shí)候?yàn)閒alse
                failed = false;
                //返回false民傻;為什么返回false 為了不調(diào)用50行---acquire方法當(dāng)中的selfInterrupt方法胰默;為什么不調(diào)用?下次解釋比較復(fù)雜
                return interrupted;
            }
            //進(jìn)到這里分為兩種情況
            //1漓踢、nc的上一個(gè)節(jié)點(diǎn)不是頭部牵署,說(shuō)白了,就是我去排隊(duì)了喧半,但是我上一個(gè)人不是隊(duì)列第一個(gè)
            //2奴迅、第二種情況,我去排隊(duì)了挺据,發(fā)覺(jué)上一個(gè)節(jié)點(diǎn)是第一個(gè)取具,但是他還在搞事沒(méi)有釋放鎖
            //不管哪種情況這個(gè)時(shí)候我都需要park,park之前我需要把上一個(gè)節(jié)點(diǎn)的狀態(tài)改成park狀態(tài)
            //這里比較難以理解為什么我需要去改變上一個(gè)節(jié)點(diǎn)的park狀態(tài)呢扁耐?每個(gè)node都有一個(gè)狀態(tài)暇检,默認(rèn)為0,表示無(wú)狀態(tài)
            //-1表示在park婉称;但是不能自己把自己改成-1狀態(tài)块仆?為什么呢?因?yàn)槟愕么_定你自己park了才是能改為-1王暗;不然你自己改成自己為-1榨乎;但是改完之后你沒(méi)有park那不就騙人?(原子性)
            //所以只能先park瘫筐;在改狀態(tài)蜜暑;但是問(wèn)題你自己都park了;完全釋放CPU資源了策肝,故而沒(méi)有辦法執(zhí)行任何代碼了肛捍,所以只能別人來(lái)改;故而可以看到每次都是自己的后一個(gè)節(jié)點(diǎn)把自己改成-1狀態(tài)
            if (shouldParkAfterFailedAcquire(p, node) &&
                //改上一個(gè)節(jié)點(diǎn)的狀態(tài)成功之后之众;自己park拙毫;到此加鎖過(guò)程說(shuō)完了
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}



    //cas將上一個(gè)節(jié)點(diǎn)的ws改為-1
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        //上一個(gè)節(jié)點(diǎn),肯定為0
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)//-1
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }



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



    static void selfInterrupt() {
        Thread.currentThread().interrupt();
    }

公平鎖lock方法到此結(jié)束棺禾,下面看看unlock方法

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


    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            //釋放鎖缀蹄,
            Node h = head;
            //如果后面還有線程等待,則h.waitStatus = -1
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }


       
        protected final boolean tryRelease(int releases) {
            //因?yàn)槭侵厝腈i,因此需要減一
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                //當(dāng)持有鎖數(shù)量為0時(shí)缺前,tc才是真正釋放了鎖
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }



    private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        Node s = node.next;
        ////若后續(xù)節(jié)點(diǎn)為空或已被cancel蛀醉,則從尾部開(kāi)始找到隊(duì)列中第一個(gè)waitStatus<=0,即未被cancel的節(jié)點(diǎn)
        if (s == null || s.waitStatus > 0) {
            s = null;
            //隊(duì)尾不為空并且隊(duì)尾不等于隊(duì)頭
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }

下面看看非公平鎖的源碼和公平鎖的區(qū)別

        //直接cas操作否則acquire(1)衅码, 公平鎖直接執(zhí)行方法acquire(1)
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }


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

這里看到公平鎖和非公平鎖的都會(huì)入隊(duì)拯刁,排隊(duì),非公平鎖會(huì)首先cas嘗試加鎖逝段,在進(jìn)行該操作垛玻,兩者還有一個(gè)區(qū)別就是acquire(arg)方法

        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                //少了一步 !hasQueuedPredecessors()  判斷自己是否需要排隊(duì)
                //公平鎖需要先判斷自己是否需要排隊(duì)范删,不需要排隊(duì)才會(huì)cas操作嘗試能否加鎖成功
                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;
        }



        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;
        }
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末譬巫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子吧雹,更是在濱河造成了極大的恐慌嘹黔,老刑警劉巖朗儒,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異参淹,居然都是意外死亡醉锄,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)浙值,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)恳不,“玉大人,你說(shuō)我怎么就攤上這事开呐⊙萄” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵筐付,是天一觀的道長(zhǎng)卵惦。 經(jīng)常有香客問(wèn)我,道長(zhǎng)瓦戚,這世上最難降的妖魔是什么沮尿? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮较解,結(jié)果婚禮上畜疾,老公的妹妹穿的比我還像新娘。我一直安慰自己印衔,他們只是感情好啡捶,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著奸焙,像睡著了一般瞎暑。 火紅的嫁衣襯著肌膚如雪彤敛。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天了赌,我揣著相機(jī)與錄音墨榄,去河邊找鬼。 笑死揍拆,一個(gè)胖子當(dāng)著我的面吹牛渠概,可吹牛的內(nèi)容都是我干的茶凳。 我是一名探鬼主播嫂拴,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼贮喧!你這毒婦竟也來(lái)了筒狠?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤箱沦,失蹤者是張志新(化名)和其女友劉穎辩恼,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體谓形,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡灶伊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了寒跳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片聘萨。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖童太,靈堂內(nèi)的尸體忽然破棺而出米辐,到底是詐尸還是另有隱情,我是刑警寧澤书释,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布翘贮,位于F島的核電站,受9級(jí)特大地震影響爆惧,放射性物質(zhì)發(fā)生泄漏狸页。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一扯再、第九天 我趴在偏房一處隱蔽的房頂上張望肴捉。 院中可真熱鬧,春花似錦叔收、人聲如沸齿穗。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)窃页。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間脖卖,已是汗流浹背乒省。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留畦木,地道東北人袖扛。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像十籍,于是被迫代替她去往敵國(guó)和親蛆封。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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

  • J.U.C Java.util.concurrent 是在并發(fā)編程中比較常用的工具類勾栗,里面包含很多用來(lái)在并發(fā)場(chǎng)景中...
    WEIJAVA閱讀 500評(píng)論 0 6
  • ReentrantLock 介紹 一個(gè)可重入的互斥鎖惨篱,它具有與使用{synchronized}方法和語(yǔ)句訪問(wèn)的隱式...
    tomas家的小撥浪鼓閱讀 4,040評(píng)論 1 4
  • 看這部分的前提是大家已經(jīng)看過(guò)AbstractQueuedSynchronizer這個(gè)類,知道它是個(gè)啥了哈围俘,如果不知...
    idolice24閱讀 282評(píng)論 0 0
  • 前言 心血來(lái)潮想到要做一些源碼分析砸讳,想想從ReentrantLock類開(kāi)始挺合適的,就嘗試著先寫(xiě)一篇吧界牡,廢話不多說(shuō)...
    海濤_meteor閱讀 293評(píng)論 0 1
  • 要理解ReentrantLock簿寂,首先要理解AbstractQueuedSynchronizer。Abstract...
    binecy閱讀 259評(píng)論 0 0