SynchronousQueue源碼分析

一、說(shuō)明:SynchronousQueue是堵塞隊(duì)列得一種,其特性是put操作和take操作必須是互相喚醒的,比如:put操作,必須take操作喚醒,take操作必須put操作喚醒,沒(méi)有take操作,多個(gè)put操作都堵塞,等待一個(gè)一個(gè)take操作來(lái)一個(gè)一個(gè)喚醒,同理,take操作也一樣,跟他的名字一樣,同步隊(duì)列,就好像,你是救我的藥,我也是救你的藥。
誤區(qū):很多人把其認(rèn)為其沒(méi)有容量,不存儲(chǔ)元素,這是錯(cuò)的。
正解: SynchronousQueue的公平隊(duì)列TransferQueue是一個(gè)單項(xiàng)鏈表,是會(huì)存儲(chǔ)元素的。

二、源碼分析
從構(gòu)造方法入手??创a注釋。
無(wú)參構(gòu)造方法,默認(rèn)給得參數(shù)是false,也就是非公平隊(duì)列。
非公平隊(duì)列使用得是TransferStack, 公平隊(duì)列使用得是TransferQueue。公平和非公平也就是put和take數(shù)據(jù)的順序是否是公平的,也就是先放則先拿,后放則后拿。

 /**
     * Creates a {@code SynchronousQueue} with nonfair access policy.
     */
// 無(wú)參構(gòu)造方法,默認(rèn)給得參數(shù)是false,也就是非公平隊(duì)列。
    public SynchronousQueue() {
        this(false);
    }
    /**
     * Creates a {@code SynchronousQueue} with the specified fairness policy.
     *
     * @param fair if true, waiting threads contend in FIFO order for
     *        access; otherwise the order is unspecified.
     */
// 非公平隊(duì)列使用得是TransferStack, 公平隊(duì)列使用得是TransferQueue。
    public SynchronousQueue(boolean fair) {
        transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
    }

這里我們研究公平隊(duì)列,也就是傳參true。
TransferQueue類(lèi)中有一個(gè)靜態(tài)內(nèi)部類(lèi)Qnode,TransferQueue中存儲(chǔ)得一個(gè)元素就是一個(gè)Qnode,Qnode有next,說(shuō)明是一個(gè)單項(xiàng)鏈表

static final class TransferQueue<E> extends Transferer<E> {
        /*
         * This extends Scherer-Scott dual queue algorithm, differing,
         * among other ways, by using modes within nodes rather than
         * marked pointers. The algorithm is a little simpler than
         * that for stacks because fulfillers do not need explicit
         * nodes, and matching is done by CAS'ing QNode.item field
         * from non-null to null (for put) or vice versa (for take).
         */

        /** Node class for TransferQueue. */
        static final class QNode {
            volatile QNode next;          // next node in queue
            volatile Object item;         // CAS'ed to or from null
            volatile Thread waiter;       // to control park/unpark
            final boolean isData;

            QNode(Object item, boolean isData) {
                this.item = item;
                this.isData = isData;
            }

TransferQueue中還有兩個(gè)變量。1、head,頭節(jié)點(diǎn)。2、tail,尾節(jié)點(diǎn)。

        /** Head of queue */
        transient volatile QNode head;
        /** Tail of queue */
        transient volatile QNode tail;
        /**
         * Reference to a cancelled node that might not yet have been
         * unlinked from queue because it was the last inserted node
         * when it was cancelled.
         */
        transient volatile QNode cleanMe;

還有三個(gè)方法。方法作用看代碼注釋。
advanceHead:cas替換,把TransferQuene的舊頭節(jié)點(diǎn)替換為新節(jié)點(diǎn),舊頭節(jié)點(diǎn)得next指向舊頭節(jié)點(diǎn)自己。
advanceTail:cas替換,把TransferQuene的舊尾節(jié)點(diǎn)替換為新節(jié)點(diǎn)。

/**
         * Tries to cas nh as new head; if successful, unlink
         * old head's next node to avoid garbage retention.
         */
// cas替換,把TransferQuene的舊頭節(jié)點(diǎn)替換為新節(jié)點(diǎn),舊頭節(jié)點(diǎn)得next指向舊頭節(jié)點(diǎn)自己。
        void advanceHead(QNode h, QNode nh) {
            if (h == head &&
                UNSAFE.compareAndSwapObject(this, headOffset, h, nh))
                h.next = h; // forget old next
        }

        /**
         * Tries to cas nt as new tail.
         */
// cas替換,把TransferQuene的舊尾節(jié)點(diǎn)替換為新節(jié)點(diǎn)。
        void advanceTail(QNode t, QNode nt) {
            if (tail == t)
                UNSAFE.compareAndSwapObject(this, tailOffset, t, nt);
        }

        /**
         * Tries to CAS cleanMe slot.
         */
// 同上
        boolean casCleanMe(QNode cmp, QNode val) {
            return cleanMe == cmp &&
                UNSAFE.compareAndSwapObject(this, cleanMeOffset, cmp, val);
        }

接下來(lái)跟著SynchronousQueue的put方法走看源碼。
調(diào)用構(gòu)造方法的時(shí)候,就創(chuàng)建了一個(gè)dummy node(假節(jié)點(diǎn)),TransferQueue的頭和尾指針都指向這個(gè)節(jié)點(diǎn)。也就是這種結(jié)構(gòu):head(tail)(dummy)

// 測(cè)試方法
    @Test
    public void testPut() throws InterruptedException {
        SynchronousQueue<String> queue = new SynchronousQueue<>(true);
        queue.put("hello");
    }
// SynchronousQueue的構(gòu)造方法,我們傳入了true,所以使用的queue是TransferQueue。
 public SynchronousQueue(boolean fair) {
        transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
    }
// 調(diào)用構(gòu)造方法的時(shí)候,就創(chuàng)建了一個(gè)dummy node(假節(jié)點(diǎn)),TransferQueue的頭和尾指針都指向這個(gè)節(jié)點(diǎn)。
    TransferQueue() {
            QNode h = new QNode(null, false); // initialize to dummy node.
            head = h;
            tail = h;
        }
static final class QNode {
        // next 指針   
         volatile QNode next;          // next node in queue
        // 實(shí)際保存的元素    
        volatile Object item;         // CAS'ed to or from null
        // 鎖和喚醒的線程    
        volatile Thread waiter;       // to control park/unpark
        // 是否是數(shù)據(jù)(put則為true,take則會(huì)false)    
        final boolean isData;
            QNode(Object item, boolean isData) {
                this.item = item;
                this.isData = isData;
            }

然后看起put方法

public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        if (transferer.transfer(e, false, 0) == null) {
            Thread.interrupted();
            throw new InterruptedException();
        }
    }
// e:put的元素。timed:是否具有超時(shí)機(jī)制。nanos:超時(shí)時(shí)間。
 E transfer(E e, boolean timed, long nanos) {
            QNode s = null; // constructed/reused as needed
            // put操作這里e不為null,isData為true
            boolean isData = (e != null);
            // 自旋
            for (;;) {
                QNode t = tail;
                QNode h = head;
                // 還沒(méi)有初始化,則continue重新來(lái)過(guò)
                if (t == null || h == null)         // saw uninitialized value
                    continue;                       // spin
                // h == t則說(shuō)明還沒(méi)有存入元素。這是為true,進(jìn)入。進(jìn)入這個(gè)if就會(huì)堵塞元素,不管take還是put。
                if (h == t || t.isData == isData) { // empty or same-mode
                    // tn為null。(順便說(shuō)一句:next變量是volatile修飾,多線程可見(jiàn),所以下面這個(gè)復(fù)制操作是線程安全的。)
                    QNode tn = t.next;
                  // 其他線程把尾節(jié)點(diǎn)改變了,則再旋一次
                    if (t != tail)                  // inconsistent read
                        continue;
                    // tn為null
                    if (tn != null) {               // lagging tail
                        advanceTail(t, tn);
                        continue;
                    }
                    // 無(wú)超時(shí)機(jī)制
                    if (timed && nanos <= 0)        // can't wait
                        return null;
                    // 創(chuàng)建put操作存放數(shù)據(jù)的新節(jié)點(diǎn),isData為true。
                    // 如果沒(méi)有元素,take操作進(jìn)入這里,也創(chuàng)建了一個(gè)新node,也就是說(shuō)take操作堵塞的時(shí)候,也會(huì)創(chuàng)建節(jié)點(diǎn),
                    if (s == null)
                        s = new QNode(e, isData);
                    // cas替換,從:head(tail)(dummy)到head(tail)(dumy)->S
                    // cas替換,尾節(jié)點(diǎn)的next指向新建立的s節(jié)點(diǎn)。失敗則再旋一次
                    if (!t.casNext(null, s))        // failed to link in
                        continue;
                    /*cas替換,把TransferQuene的舊尾節(jié)點(diǎn)t替換為新節(jié)點(diǎn)s。
                        至此,變成了head(dumy)=>s(tail)
                    */
                    advanceTail(t, s);              // swing tail and wait
                    // 下面這個(gè)方法調(diào)用就是具體的堵塞調(diào)用,看下一段代碼分析
                    Object x = awaitFulfill(s, e, timed, nanos);
                    if (x == s) {                   // wait was cancelled
                        clean(t, s);
                        return null;
                    }
                    if (!s.isOffList()) {           // not already unlinked
                        advanceHead(t, s);          // unlink if head
                        if (x != null)              // and forget fields
                            s.item = s;
                        s.waiter = null;
                    }
                    return (x != null) ? (E)x : e;

                } else {                            // complementary-mode
                    QNode m = h.next;               // node to fulfill
                    if (t != tail || m == null || h != head)
                        continue;                   // inconsistent read

                    Object x = m.item;
                    if (isData == (x != null) ||    // m already fulfilled
                        x == m ||                   // m cancelled
                        !m.casItem(x, e)) {         // lost CAS
                        advanceHead(h, m);          // dequeue and retry
                        continue;
                    }

                    advanceHead(h, m);              // successfully fulfilled
                    LockSupport.unpark(m.waiter);
                    return (x != null) ? (E)x : e;
                }
            }
        }
 /**
         * Spins/blocks until node s is fulfilled.
         *
         * @param s the waiting node
         * @param e the comparison value for checking match
         * @param timed true if timed wait
         * @param nanos timeout value
         * @return matched item, or s if cancelled
         */
// s是新節(jié)點(diǎn),e是put的真正數(shù)據(jù),timed是是否超時(shí),nanos是超時(shí)時(shí)間
 Object awaitFulfill(QNode s, E e, boolean timed, long nanos) {
            /* Same idea as TransferStack.awaitFulfill */
          // deadLine為0
            final long deadline = timed ? System.nanoTime() + nanos : 0L;
            Thread w = Thread.currentThread();
            // 自旋次數(shù),第一次put的時(shí)候,鏈表結(jié)構(gòu)為:head(dumy)=>s(tail),所以
// head.hext == s,timed為不超時(shí)false,所以spin=maxUntimedSpins=512,
// 如果第二個(gè)put堵塞,則結(jié)構(gòu)為:head(dumy)=>s=>s1(tail),而head.next和s1(新put的元素是s1)不相等,所以,spin=0直接堵塞。
//(為什么會(huì)自旋,我估計(jì)是為了,高并發(fā)下,take操作
// 和put操作很可能再極短時(shí)間進(jìn)行,這樣的話(huà),就不需要喚醒線程和堵塞線程)
            int spins = ((head.next == s) ?
                         (timed ? maxTimedSpins : maxUntimedSpins) : 0);
            for (;;) {
                // 當(dāng)前線程準(zhǔn)備中斷,則s節(jié)點(diǎn)的item指向s節(jié)點(diǎn)自己
                if (w.isInterrupted())
                    s.tryCancel(e);
                Object x = s.item;
              // 這里為false,只有當(dāng)其他線程把這個(gè)元素替換了,比如put堵塞在這
// 里的時(shí)候,take就會(huì)把這個(gè)元素替換掉,然后put喚醒的時(shí)候就能直接return了。
                if (x != e)
                    return x;
                if (timed) {
                    nanos = deadline - System.nanoTime();
                    if (nanos <= 0L) {
                        s.tryCancel(e);
                        continue;
                    }
                }
                // 這里spins初始為512,一直遞減到0
                if (spins > 0)
                    --spins;
                else if (s.waiter == null)
                    // node節(jié)點(diǎn)和waiter和當(dāng)前線程關(guān)聯(lián)上,為了公平的喚醒。
                    s.waiter = w;
                else if (!timed)
                    // 鎖住當(dāng)前線程。設(shè)置thread的block變量為parkBlocker指向的對(duì)象transferQueue。
                    LockSupport.park(this);
                else if (nanos > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanos);
            }
        }

再來(lái)看take方法

public E take() throws InterruptedException {
        E e = transferer.transfer(null, false, 0);
        if (e != null)
            return e;
        Thread.interrupted();
        throw new InterruptedException();
    }
E transfer(E e, boolean timed, long nanos) {
           QNode s = null; // constructed/reused as needed
           // isData為false
           boolean isData = (e != null);
           for (;;) {
               QNode t = tail;
               QNode h = head;
               if (t == null || h == null)         // saw uninitialized value
                   continue;                       // spin
             // 第一put后結(jié)構(gòu)變成了:head(dumy)=>s(tail)   
             /* 所以這里h不等于t,t.isData為true,
              *所以這里不成立,走else塊。只要不堵塞,都會(huì)走else塊。put操作如 
              果不堵塞,也會(huì)走else塊。
                */
               if (h == t || t.isData == isData) { // empty or same-mode
                   QNode tn = t.next;
                   if (t != tail)                  // inconsistent read
                       continue;
                   if (tn != null) {               // lagging tail
                       advanceTail(t, tn);
                       continue;
                   }
                   if (timed && nanos <= 0)        // can't wait
                       return null;
                   if (s == null)
                       s = new QNode(e, isData);
                   if (!t.casNext(null, s))        // failed to link in
                       continue;

                   advanceTail(t, s);              // swing tail and wait
                   Object x = awaitFulfill(s, e, timed, nanos);
                   if (x == s) {                   // wait was cancelled
                       clean(t, s);
                       return null;
                   }

                   if (!s.isOffList()) {           // not already unlinked
                       advanceHead(t, s);          // unlink if head
                       if (x != null)              // and forget fields
                           s.item = s;
                       s.waiter = null;
                   }
                   return (x != null) ? (E)x : e;

               } else {                            // complementary-mode
                 //head(dumy)=>s(tail)
                  // m就是之前put的哪個(gè)節(jié)點(diǎn) 
                  QNode m = h.next;               // node to fulfill
                   if (t != tail || m == null || h != head)
                       continue;                   // inconsistent read
                   Object x = m.item;
                    /* 
isData=(e!=null),這是take操作,所以e為null,所以isData為false,x為之前put進(jìn)去的值,為非null
x == m說(shuō)明已經(jīng)取消了,之前put操作的時(shí)候,
                   *awaitFulfill方法里,如果當(dāng)前線程準(zhǔn)備中斷,
                   *就會(huì)調(diào)用qnode的tryCancel方法,讓qnode的next指向自己,代表 
                    * 這個(gè)節(jié)點(diǎn)取消了。
                     head(dumy)=>s(tail)
                     *!m.casItem(x, e):直接替換item的值,
                        這樣,在take方法堵塞在awaitFulfill方法里的時(shí)候,
                     這里直接把之前take方法創(chuàng)建的node的item改掉,
                     然后喚醒take的線程,然后take操作獲取到這個(gè)新值了和它之前的值不一樣,則直接跳出循環(huán),不堵塞。
                      */
                   if (isData == (x != null) ||    // m already fulfilled
                       x == m ||                   // m cancelled
                       !m.casItem(x, e)) {         // lost CAS
                       advanceHead(h, m);          // dequeue and retry
                       continue;
                   }
                 // 變成了head(s)(tail)                    
                   advanceHead(h, m);              // successfully fulfilled
                   LockSupport.unpark(m.waiter);
                   return (x != null) ? (E)x : e;
               }
           }
       }

SynchronousQueue還有offer方法,poll方法。add方法。
offer方法,不會(huì)堵塞,可以存進(jìn)去,則存進(jìn)去(poll或take操作正在堵塞等著獲取元素),否則,直接返回false。
poll方法可以有超時(shí)時(shí)間,和take差不多,take沒(méi)有超時(shí)時(shí)間。
add方法,調(diào)用的就是offer方法,不過(guò)add方法,添加不進(jìn)去,則直接報(bào)錯(cuò)。

總結(jié)
新建TransQueue初始化結(jié)構(gòu)為:head(dumy)(tail)
第一個(gè)次put后:head(dumy)=>s(tail)
第二個(gè)次put后:head(dumy)=>s=>s1(tail)

第一次put后緊接著take后:head(s)(tail)

鏈表中沒(méi)有put過(guò)的節(jié)點(diǎn),則自旋512次,有,則直接堵塞

put和take都會(huì)存儲(chǔ)元素,take存儲(chǔ)的元素item為null,再堵塞之后,put操作會(huì)cas替換這個(gè)元素的item,然后喚醒take操作,獲取這個(gè)新元素。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,582評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事?!?“怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,540評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,028評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,801評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,223評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,442評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,976評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,800評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,996評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評(píng)論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,233評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,662評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,926評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,702評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評(píng)論 2 374

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