AQS

一、什么是AQS

JAVA中痕貌,在多線程環(huán)境下风罩,一般需要鎖來確保數(shù)據(jù)安全,而我們通常所說的鎖舵稠,其實(shí)分為了兩類超升,一類是sychronized,利用的是指令級別的monitor-enter 和 monitor-exit哺徊;另一類是Lock室琢,例如ReentrantLock等,是依靠代碼實(shí)現(xiàn)的落追。
而AQS盈滴,即AbstractQueuedSynchronizer,又叫隊(duì)列同步器淋硝,就是用來構(gòu)建鎖和其他同步組件的基礎(chǔ)框架雹熬。
AQS是一個(gè)以繼承方式使用的抽象類,他維護(hù)了一個(gè)volatile的state和一個(gè)FIFO線程等待隊(duì)列谣膳,類們必須定義改變state變量的protected方法竿报,這些方法定義了state是如何被獲取或釋放的。

二继谚、AQS原理簡介

2.1 status狀態(tài)

AQS使用int類型來保存同步狀態(tài)烈菌,并且該狀態(tài)變量(status)被聲明為是volatile變量,并暴露出getState花履、setState以及compareAndSet操作來讀取和更新這個(gè)同步狀態(tài)芽世,由于compareAndSet是CAS操作,且變量本身是volatile變量诡壁,所以通過暴露這三個(gè)方法就達(dá)到了同步狀態(tài)的原子性管理济瓢,確保了同步狀態(tài)的原子性、可見性和有序性妹卿。

2.2 阻塞 LockSupport

AQS也是調(diào)用的LockSupport.park阻塞當(dāng)前線程直到有個(gè)LockSupport.unpark方法被調(diào)用旺矾。

LockSupport

在Java多線程中,當(dāng)需要阻塞或者喚醒一個(gè)線程時(shí)夺克,都會(huì)使用LockSupport工具類來完成相應(yīng)的工作箕宙。LockSupport定義了一組公共靜態(tài)方法,這些方法提供了最基本的線程阻塞和喚醒功能铺纽,而LockSupport也因此成為了構(gòu)建同步組件的基礎(chǔ)工具柬帕。
LockSupport定義了一組以park開頭的方法用來阻塞當(dāng)前線程,以及unpark(Thread)方法來喚醒一個(gè)被阻塞的線程,這些方法描述如下:
1.void park() 阻塞當(dāng)前線程陷寝,如果調(diào)用unpark(Thread)方法或被中斷锅很,才能從park()返回
2.void parkNanos(long nanos) 阻塞當(dāng)前線程,超時(shí)返回盼铁,阻塞時(shí)間最長不超過nanos納秒
3.void parkUntil(long deadline) 阻塞當(dāng)前線程粗蔚,直到deadline時(shí)間點(diǎn)
4.void unpark(Thread) 喚醒處于阻塞狀態(tài)的線程
5.park(Object blocker) 阻塞當(dāng)前線程,參數(shù)blocker是用來標(biāo)識當(dāng)前線程在等待的對象饶火,該對象主要用于問題排查和系統(tǒng)監(jiān)控
6.parkNanos(Object blocker, long nanos) 阻塞當(dāng)前線程鹏控,超時(shí)返回,阻塞時(shí)間最長不超過nanos納秒肤寝,參數(shù)blocker是用來標(biāo)識當(dāng)前線程在等待的對象当辐,該對象主要用于問題排查和系統(tǒng)監(jiān)控
7.parkUntil(Object blocker, long deadline) 阻塞當(dāng)前線程,直到deadline時(shí)間點(diǎn)鲤看,參數(shù)blocker是用來標(biāo)識當(dāng)前線程在等待的對象缘揪,該對象主要用于問題排查和系統(tǒng)監(jiān)控

2.3 同步隊(duì)列

AQS整個(gè)框架的核心就是如何管理線程阻塞隊(duì)列,他通過內(nèi)置的FIFO雙向隊(duì)列來完成線程的排隊(duì)工作义桂。該隊(duì)列內(nèi)部通過結(jié)點(diǎn)head和tail記錄隊(duì)首和隊(duì)尾元素,而該隊(duì)列的元素類型均為一個(gè)內(nèi)部類Node找筝。

Node
 static final class Node {
        /** Marker to indicate a node is waiting in shared mode */
        static final Node SHARED = new Node();
        /** Marker to indicate a node is waiting in exclusive mode */
        static final Node EXCLUSIVE = null;

        /** waitStatus value to indicate thread has cancelled */
        static final int CANCELLED =  1;
        /** waitStatus value to indicate successor's thread needs unparking */
        static final int SIGNAL    = -1;
        /** waitStatus value to indicate thread is waiting on condition */
        static final int CONDITION = -2;
        /**
         * waitStatus value to indicate the next acquireShared should
         * unconditionally propagate
         */
        static final int PROPAGATE = -3;

        volatile int waitStatus;

        volatile Node prev;

        volatile Node next;

        volatile Thread thread;

        Node nextWaiter;

        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    // Used to establish initial head or SHARED marker
        }

        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

先看下waitStatus的幾種狀態(tài)
1.CANCELLED=1 表示線程因?yàn)橹袛嗷蛘叩却瑫r(shí),需要從等待隊(duì)列中取消等待慷吊,被取消的節(jié)點(diǎn)會(huì)被踢出隊(duì)列袖裕;
2.SIGNAL=-1 表示后續(xù)節(jié)點(diǎn)的線程處于等待狀態(tài),當(dāng)前節(jié)點(diǎn)的線程如果釋放了同步狀態(tài)或者被取消溉瓶,會(huì)通知后繼節(jié)點(diǎn)急鳄,后繼節(jié)點(diǎn)會(huì)獲取鎖并執(zhí)行(當(dāng)一個(gè)節(jié)點(diǎn)的狀態(tài)為SIGNAL時(shí)就意味著在等待獲取同步狀態(tài),前節(jié)點(diǎn)是頭節(jié)點(diǎn)也就是獲取同步狀態(tài)的節(jié)點(diǎn))堰酿;
3.CONDITION=-2 表示節(jié)點(diǎn)在條件等待隊(duì)列中疾宏,當(dāng)其他線程調(diào)用了Condition的signal()方法之后,該節(jié)點(diǎn)就會(huì)從條件等待隊(duì)列移動(dòng)到同步隊(duì)列中触创;
4.PROPAGTE=-3 表示下一次共享式同步狀態(tài)獲取將會(huì)傳遞給后繼節(jié)點(diǎn)獲取這個(gè)共享同步狀態(tài)(讀寫鎖中存在的狀態(tài)坎藐,代表后續(xù)還有資源,可以多個(gè)線程同時(shí)擁有同步狀態(tài))哼绑;

入隊(duì)操作
同步隊(duì)列入隊(duì)操作.jpg

如圖所示顺饮,入隊(duì)的操作主要分為兩步
1.為了確保線程安全,同步器提供了一個(gè)CAS方法凌那,它需要傳遞當(dāng)前線程“認(rèn)為”的尾節(jié)點(diǎn)和當(dāng)前節(jié)點(diǎn),將"認(rèn)為"的尾結(jié)點(diǎn)和實(shí)際的尾結(jié)點(diǎn)比對成功才會(huì)將尾結(jié)點(diǎn)和新入隊(duì)的節(jié)點(diǎn)進(jìn)行關(guān)聯(lián)
2.上述CAS操作成功后吟逝,當(dāng)前節(jié)點(diǎn)才正式與之前的尾結(jié)點(diǎn)建立關(guān)聯(lián)

出隊(duì)操作
同步隊(duì)列出隊(duì)操作.jpg

遵循FIFO規(guī)則帽蝶,能成功獲取到AQS同步狀態(tài)的必定是首節(jié)點(diǎn),故和入隊(duì)操作不同,出隊(duì)操作沒必要CAS處理励稳,只需要將首節(jié)點(diǎn)設(shè)置為原首節(jié)點(diǎn)的后續(xù)節(jié)點(diǎn)同時(shí)斷開原節(jié)點(diǎn)佃乘、后續(xù)節(jié)點(diǎn)的引用即可。

2.4 條件隊(duì)列

前文介紹node節(jié)點(diǎn)的waitStatus時(shí)提到驹尼,當(dāng)值為-2時(shí)趣避,表示節(jié)點(diǎn)在條件隊(duì)列中,也就是說不同于前文的同步隊(duì)列新翎,在AQS中還存在一個(gè)條件隊(duì)列程帕,也就是一個(gè)ConditionObject的內(nèi)部類,如下圖


圖片.png

ConditionObject實(shí)現(xiàn)了Condition接口地啰,Condition接口提供了類似Object管程式的方法愁拭,如await、signal和signalAll操作亏吝,這里對Condition接口的方法做下簡單的介紹

//當(dāng)前線程進(jìn)入等待狀態(tài)岭埠,直到被通知(signal)或被中斷
void await() throws InterruptedException;
//當(dāng)前線程進(jìn)入等待狀態(tài),直到被通知(signal),對于中斷不做響應(yīng)
void awaitUninterruptibly();
//當(dāng)前線程進(jìn)入等待狀態(tài)蔚鸥,等待指定時(shí)長(單位為毫秒)惜论,直到被通知、中斷止喷,或者超時(shí)
long awaitNanos(long nanosTimeout) throws InterruptedException;
//當(dāng)前線程進(jìn)入等待狀態(tài)馆类,等待指定時(shí)長,直到被通知启盛、中斷蹦掐,或者超時(shí)
boolean await(long time, TimeUnit unit) throws InterruptedException;
//當(dāng)前線程進(jìn)入等待狀態(tài),直到被通知僵闯、中斷卧抗,或者到達(dá)指定時(shí)間。如果沒有到指定時(shí)間就被通知鳖粟,方法返回true社裆,否則false
boolean awaitUntil(Date deadline) throws InterruptedException;
// 喚醒一個(gè)等待線程,該線程從等待方法返回前必須獲得與Condition相關(guān)聯(lián)的鎖 
void signal();
// 喚醒所有等待線程向图,該線程從等待方法返回前必須獲得與Condition相關(guān)聯(lián)的鎖  
void signalAll();

ConditionObject中存在一個(gè)firstWaiter和一個(gè)lastWaiter泳秀,也就是說他有自己單獨(dú)的隊(duì)列,signal操作是通過將節(jié)點(diǎn)從條件隊(duì)列轉(zhuǎn)移到同步隊(duì)列中來實(shí)現(xiàn)的榄攀,而await操作就是當(dāng)前線程節(jié)點(diǎn)從同步隊(duì)列進(jìn)入條件隊(duì)列進(jìn)行等待嗜傅。
AQS只有一個(gè)同步隊(duì)列,但是可以有多個(gè)條件隊(duì)列檩赢。

2.5 資源共享方式

在前文給出Node節(jié)點(diǎn)的源碼時(shí)吕嘀,可以發(fā)現(xiàn)在Node節(jié)點(diǎn)中還存在SHARED 和 EXCLUSIVE 兩個(gè)靜態(tài)常量。是的,AQS定義兩種資源共享方式:Exclusive(獨(dú)占偶房,只有一個(gè)線程能執(zhí)行趁曼,如ReentrantLock)和Share(共享,多個(gè)線程可同時(shí)執(zhí)行棕洋,如Semaphore/CountDownLatch)挡闰。
不同的自定義同步器爭用共享資源的方式也不同。自定義同步器在實(shí)現(xiàn)時(shí)只需要實(shí)現(xiàn)共享資源state的獲取與釋放方式即可掰盘。
自定義同步器實(shí)現(xiàn)時(shí)主要實(shí)現(xiàn)以下幾種方法:
1.isHeldExclusively():該線程是否正在獨(dú)占資源摄悯。只有用到condition才需要去實(shí)現(xiàn)它。
2.tryAcquire(int):獨(dú)占方式庆杜。嘗試獲取資源射众,成功則返回true,失敗則返回false晃财。
3.tryRelease(int):獨(dú)占方式叨橱。嘗試釋放資源,成功則返回true断盛,失敗則返回false罗洗。
4.tryAcquireShared(int):共享方式。嘗試獲取資源钢猛。負(fù)數(shù)表示失敾锊恕;0表示成功命迈,但沒有剩余可用資源贩绕;正數(shù)表示成功,且有剩余資源壶愤。
5.tryReleaseShared(int):共享方式淑倾。嘗試釋放資源,如果釋放后允許喚醒后續(xù)等待結(jié)點(diǎn)返回true征椒,否則返回false娇哆。

三、AQS源碼分析

以上分析了大概的原理勃救,接下來看看主要的幾個(gè)方法的源碼

3.1 acquire(int)

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

acquire是一種以獨(dú)占方式獲取資源,如果獲取到了碍讨,就會(huì)直接返回,否則會(huì)被添加到隊(duì)列中進(jìn)行等待蒙秒,直到獲取到資源為止勃黍,并且這個(gè)操作是忽略中斷的。通過上面的源碼可以看到晕讲,該方法調(diào)用了以下幾個(gè)方法
1.tryAcquire() 用于獲取資源覆获,如果獲取到了就直接返回
2.addWaiter() 添加到同步等待隊(duì)列的隊(duì)尾榜田,這里傳入的Node.EXCLUSIVE表示是獨(dú)占模式
3.acquireQueued() 使線程在同步隊(duì)列中進(jìn)行等待,獲取到資源后才會(huì)返回锻梳,整個(gè)等待過程中被中斷過,則返回true净捅,否則返回false疑枯。
4.selfInterrupt() 獲取到資源后,如果acquireQueued() 被中斷過蛔六,則if條件判斷為空荆永,會(huì)進(jìn)到這里進(jìn)行自我中斷

詳細(xì)來看看

3.1.1tryAcquire()
  protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

默認(rèn)的實(shí)現(xiàn)只是單純拋了個(gè)異常,具體的需要子類進(jìn)行自定義擴(kuò)展国章,該方法表示嘗試以獨(dú)占的方式直接獲取資源具钥,如果獲取成功,則直接返回true液兽,否則直接返回false

3.1.2 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;
    }

首先獲取到隊(duì)列的尾結(jié)點(diǎn)骂删。
如果尾結(jié)點(diǎn)不為空,則通過compareAndSetTail(pred, node)方法以CAS操作將尾結(jié)點(diǎn)設(shè)置為當(dāng)前節(jié)點(diǎn)四啰,如果設(shè)置成功宁玫,則將當(dāng)前節(jié)點(diǎn)和前節(jié)點(diǎn)進(jìn)行關(guān)聯(lián)。
如果尾結(jié)點(diǎn)為空柑晒,則表示隊(duì)列還未初始化欧瘪,此時(shí)會(huì)調(diào)用enq()方法,這里也貼出enq方法的源碼

 private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

可以看到這里采用CAS自旋的方式初始化隊(duì)列匙赞,第一次循環(huán)時(shí)會(huì)將頭結(jié)點(diǎn)和尾結(jié)點(diǎn)均設(shè)置為一個(gè)空的Node(new Node())佛掖,第二次循環(huán)時(shí)進(jìn)入else分支,將當(dāng)前節(jié)點(diǎn)設(shè)置成為尾結(jié)點(diǎn)涌庭。

3.1.3acquireQueued()
   final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    //如果是頭結(jié)點(diǎn)且獲取資源成功
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

可以看到該方法分為兩部分芥被,第一部分為如果是頭結(jié)點(diǎn)則直接嘗試獲取資源,如果獲取成功則返回脾猛,第二部分為如果不是頭結(jié)點(diǎn)或者資源沒有獲取成功撕彤,則會(huì)判斷當(dāng)前節(jié)點(diǎn)是否應(yīng)該park以及檢查是否中斷。
該方法實(shí)際是一個(gè)自旋猛拴,只有當(dāng)拿到鎖之后才會(huì)返回羹铅。接下來進(jìn)一步的看看shouldParkAfterFailedAcquire和parkAndCheckInterrupt

shouldParkAfterFailedAcquire()
 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            /*
             * 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;
    }

如果前節(jié)點(diǎn)是SIGNAL狀態(tài)牌里,表示當(dāng)前節(jié)點(diǎn)應(yīng)該處于等待狀態(tài)借卧,需要中斷,如果前節(jié)點(diǎn)狀態(tài)>0赊琳,表示前節(jié)點(diǎn)已被取消跛溉,則會(huì)剔除前面所有取消的節(jié)點(diǎn)(循環(huán)將第一個(gè)不是取消狀態(tài)的節(jié)點(diǎn)設(shè)置為當(dāng)前節(jié)點(diǎn)的前節(jié)點(diǎn))焊切,其他狀態(tài)則將當(dāng)前節(jié)點(diǎn)設(shè)置為SIGNAL狀態(tài)扮授,表示當(dāng)前節(jié)點(diǎn)可以獲取同步狀態(tài)。

parkAndCheckInterrupt()
private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

讓線程waiting专肪,調(diào)用park方法后刹勃,除非被中斷或者unpark才會(huì)被喚醒,然后通過Thread.interrupted()清除線程的中斷標(biāo)記嚎尤。

所以回顧一下就會(huì)發(fā)現(xiàn)荔仁,整個(gè)acquire()方法的過程為
1.tryAcquire() 用于獲取資源,如果獲取到了就直接返回
2.addWaiter() 以Node.EXCLUSIVE獨(dú)占模式添加到同步等待隊(duì)列的隊(duì)尾芽死,如果隊(duì)列為空則首先將隊(duì)頭和隊(duì)尾設(shè)置為一個(gè)new Node()然后將當(dāng)前節(jié)點(diǎn)添加進(jìn)來
3.acquireQueued() 使線程在同步隊(duì)列中進(jìn)行等待乏梁,獲取到資源后才會(huì)返回,如果當(dāng)前節(jié)點(diǎn)是隊(duì)頭关贵,則直接嘗試獲取資源遇骑,成功后返回,否則會(huì)檢查當(dāng)前節(jié)點(diǎn)的前節(jié)點(diǎn)的狀態(tài)揖曾,以判斷當(dāng)前節(jié)點(diǎn)的線程是否應(yīng)該被park落萎,如果整個(gè)等待過程中被中斷過,則返回true翩肌,否則返回false模暗。
4.selfInterrupt() 獲取到資源后,如果acquireQueued() 被中斷過念祭,則if條件判斷為空兑宇,會(huì)進(jìn)到這里進(jìn)行自我中斷

3.2 release()

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

release方法比較簡單,直接通過tryRelease來釋放資源粱坤,如果成功了則喚醒頭結(jié)點(diǎn)的后續(xù)節(jié)點(diǎn)隶糕,unparkSuccessor中unpark了后繼節(jié)點(diǎn)的線程,如下面貼上的代碼所示

protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }

 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.3 acquireShared()

  public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

  private void doAcquireShared(int arg) {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

 private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // Record old head for check below
        setHead(node);
        /*
         * Try to signal next queued node if:
         *   Propagation was indicated by caller,
         *     or was recorded (as h.waitStatus either before
         *     or after setHead) by a previous operation
         *     (note: this uses sign-check of waitStatus because
         *      PROPAGATE status may transition to SIGNAL.)
         * and
         *   The next node is waiting in shared mode,
         *     or we don't know, because it appears null
         *
         * The conservatism in both of these checks may cause
         * unnecessary wake-ups, but only when there are multiple
         * racing acquires/releases, so most need signals now or soon
         * anyway.
         */
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }

可以看到站玄,acquireShared也會(huì)直接嘗試獲取資源枚驻,如果獲取失敗則進(jìn)入在doAcquireShared中首先以Node.SHARED的狀態(tài)添加到隊(duì)尾,同時(shí)當(dāng)前節(jié)點(diǎn)的前節(jié)點(diǎn)(node.predecessor()獲取)是head時(shí)株旷,會(huì)再次獲取資源再登,和獨(dú)占模式不同的是,如果獲取成功晾剖,且剩余資源>0锉矢,會(huì)通過調(diào)用setHeadAndPropagate()來釋放剩余資源并喚醒后續(xù)節(jié)點(diǎn)線程, doReleaseShared()后文會(huì)說明齿尽。

3.4 releaseShared()

public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

  private void doReleaseShared() {
        /*
         * Ensure that a release propagates, even if there are other
         * in-progress acquires/releases.  This proceeds in the usual
         * way of trying to unparkSuccessor of head if it needs
         * signal. But if it does not, status is set to PROPAGATE to
         * ensure that upon release, propagation continues.
         * Additionally, we must loop in case a new node is added
         * while we are doing this. Also, unlike other uses of
         * unparkSuccessor, we need to know if CAS to reset status
         * fails, if so rechecking.
         */
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

簡單來說就是釋放資源喚醒后續(xù)節(jié)點(diǎn)沽损,但是和獨(dú)占模式不同的是,獨(dú)占模式在當(dāng)前資源釋放成功后循头,如果頭結(jié)點(diǎn)的狀態(tài)不等于0就會(huì)喚醒頭結(jié)點(diǎn)的線程绵估,而共享模式下炎疆,就算頭結(jié)點(diǎn)的狀態(tài)已經(jīng)是0了,還會(huì)用CAS操作將同步狀態(tài)傳遞下去国裳,除非傳遞失敗形入。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市缝左,隨后出現(xiàn)的幾起案子唯笙,更是在濱河造成了極大的恐慌,老刑警劉巖盒使,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異七嫌,居然都是意外死亡少办,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門诵原,熙熙樓的掌柜王于貴愁眉苦臉地迎上來英妓,“玉大人,你說我怎么就攤上這事绍赛÷溃” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵吗蚌,是天一觀的道長腿倚。 經(jīng)常有香客問我,道長蚯妇,這世上最難降的妖魔是什么敷燎? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮箩言,結(jié)果婚禮上硬贯,老公的妹妹穿的比我還像新娘。我一直安慰自己陨收,他們只是感情好饭豹,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著务漩,像睡著了一般拄衰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上菲饼,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天肾砂,我揣著相機(jī)與錄音,去河邊找鬼宏悦。 笑死镐确,一個(gè)胖子當(dāng)著我的面吹牛包吝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播源葫,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼诗越,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了息堂?” 一聲冷哼從身側(cè)響起嚷狞,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎荣堰,沒想到半個(gè)月后床未,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡振坚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年薇搁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片渡八。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡啃洋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出屎鳍,到底是詐尸還是另有隱情宏娄,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布逮壁,位于F島的核電站孵坚,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏窥淆。R本人自食惡果不足惜十饥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望祖乳。 院中可真熱鬧逗堵,春花似錦、人聲如沸眷昆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亚斋。三九已至作媚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間帅刊,已是汗流浹背纸泡。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赖瞒,地道東北人女揭。 一個(gè)月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓蚤假,卻偏偏與公主長得像,于是被迫代替她去往敵國和親吧兔。 傳聞我的和親對象是個(gè)殘疾皇子磷仰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

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