J.U.C 阻塞隊(duì)列源碼剖析系列(四)之 SynchronousQueue

上一篇文章剖析了 LinkedBlockingQueue 的相關(guān)源碼,那這篇文章接著看另外一個(gè)常見的阻塞隊(duì)列 —— SynchronousQueue

簡(jiǎn)介

SynchronousQueue 是一個(gè)比較特殊的阻塞隊(duì)列類,為什么這樣說呢?我們不妨從官方的類注釋說起...

根據(jù)類注釋可大概得出以下幾點(diǎn):

  • 每一個(gè)插入操作都必須等待另一個(gè)線程完成刪除操作
  • 隊(duì)列沒有內(nèi)部容量,所以不能迭代數(shù)據(jù)
  • 可以選擇公平策略。公平策略是使用隊(duì)列先入先出,非公平策略是使用堆棧先入后出

咦?SynchronousQueue 對(duì)象沒有容量,那這個(gè)阻塞隊(duì)列的使用場(chǎng)景是什么呢?
其實(shí)線程池的其中一種實(shí)現(xiàn)——Executors.newCachedThreadPool就使用了SynchronousQueue作為阻塞隊(duì)列

那先從一個(gè)demo開始揭開 SynchronousQueue 的廬山真面目吧!

public class SynchronousQueueDemo {
    public static void main(String[] args) throws InterruptedException {
        SynchronousQueue synchronousQueue = new SynchronousQueue();
        new Thread(() -> {
            try {
                synchronousQueue.put("Hello World!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "線程一").start();
        new Thread(() -> {
            try {
                System.out.println(synchronousQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "線程二").start();
    }
}

示例中為什么在main方法里使用兩個(gè)線程分別執(zhí)行put和take操作呢?因?yàn)樵谕痪€程中,有可能存在先執(zhí)行take操作,當(dāng)程序執(zhí)行take方法的時(shí)候發(fā)現(xiàn)隊(duì)列為空就會(huì)阻塞當(dāng)前線程,那么之后的put方法就不會(huì)執(zhí)行,線程將會(huì)一直等待。

源碼剖析

成員變量

    // SynchronousQueue 定義的抽象類,由 TransferStack 和 TransferQueue 實(shí)現(xiàn)
    private transient volatile Transferer<E> transferer;

    // CPU 數(shù)量
    static final int NCPUS = Runtime.getRuntime().availableProcessors();

    // 自旋次數(shù),如果transfer指定了timeout時(shí)間,則使用maxTimeSpins,如果CPU數(shù)量小于2則自旋次數(shù)為0,否則為32。不會(huì)隨CPU數(shù)量增加而變化
    static final int maxTimedSpins = (NCPUS < 2) ? 0 : 32;

    // 自旋次數(shù),如果沒有指定時(shí)間設(shè)置,則使用maxUntimedSpins。如果NCPUS數(shù)量大于等于2則設(shè)定為為32*16,否則為0
    static final int maxUntimedSpins = maxTimedSpins * 16;

    // 為了防止自定義的時(shí)間限過長(zhǎng),為了優(yōu)化而設(shè)置,如果自定義時(shí)間長(zhǎng)于這個(gè)值則取默認(rèn)的 spinForTimeoutThreshold ,單位為納秒。
    static final long spinForTimeoutThreshold = 1000L;

構(gòu)造函數(shù)

    // 默認(rèn)使用非公平策略
    public SynchronousQueue() {
        this(false);
    }

    // fair為false是非公平策略,使用的數(shù)據(jù)結(jié)構(gòu)是棧;fair為true是公平策略,使用的數(shù)據(jù)結(jié)構(gòu)是隊(duì)列
    public SynchronousQueue(boolean fair) {
        transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
    }

先列出相關(guān)方法的源碼,但并沒有加上注釋,因?yàn)楹诵姆椒ǘ荚?Transferer 對(duì)象中聲明?。ㄖ档米⒁獾氖?,SynchronousQueue類并沒有實(shí)現(xiàn)remove、removeAll、peek、clear等方法,都是使用默認(rèn)值)

添加(add、offer、put)、刪除、查找元素

    public boolean add(E e) {
        if (offer(e))
            return true;
        else
            throw new IllegalStateException("Queue full");
    }
    public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        if (transferer.transfer(e, true, unit.toNanos(timeout)) != null)
            return true;
        if (!Thread.interrupted())
            return false;
        throw new InterruptedException();
    }

    public boolean offer(E e) {
        if (e == null) throw new NullPointerException();
        return transferer.transfer(e, true, 0) != null;
    }
    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();
        }
    }
    
    public E poll() {
        return transferer.transfer(null, true, 0);
    }
    public E take() throws InterruptedException {
        E e = transferer.transfer(null, false, 0);
        if (e != null)
            return e;
        Thread.interrupted();
        throw new InterruptedException();
    }

相對(duì)于 ArrayBlockingQueue 和 LinkedBlockingQueue,可以發(fā)現(xiàn)類似poll、take等相關(guān)方法都被抽象成統(tǒng)一方法來進(jìn)行操作,通過抽象出內(nèi)部類 Transferer 實(shí)現(xiàn)不同的操作。接下來,咱們重點(diǎn)看看公平模式與非公平模式下的源碼。

1.SynchronousQueue 的非公平模式(TransferStack)

眾所周知,堆棧是FILO(First in last out)的方式,所以也可以理解為非公平模式為什么使用棧這種數(shù)據(jù)結(jié)構(gòu),如果排隊(duì)的時(shí)候第一個(gè)進(jìn)來,最后一個(gè)才能走,這很不公平嘛!

在 TransferStack 內(nèi)部有 REQUEST、DATA、FULFILLING 這三個(gè)狀態(tài)。

REQUEST 表示請(qǐng)求從棧獲取數(shù)據(jù)操作的消費(fèi)者,如:take 方法;
DATA 表示往棧內(nèi)部放數(shù)據(jù)的生產(chǎn)者,如:put 方法;
FULFILLING 表示正在交易的生產(chǎn)者或消費(fèi)者

REQUEST 和 DATA 這兩種狀態(tài)理解起來還不難,但或許 FULFILLING 還是不太清楚有什么用,先帶著疑問往下去看,現(xiàn)在只需要簡(jiǎn)單的理解為:不同狀態(tài) REQUEST 和 DATA 可以相互匹配的,當(dāng)與棧頂匹配后就會(huì)將他們狀態(tài)轉(zhuǎn)換為 FULFILLING,當(dāng)匹配成功后就會(huì)將棧頂和匹配的元素一同出棧。

成員變量

   //表示一個(gè)未填充的消費(fèi)者
   static final int REQUEST = 0;
   //表示一個(gè)未填充的生產(chǎn)者
   static final int DATA = 1;
   // 表示生產(chǎn)者正在給等待資源的消費(fèi)者補(bǔ)給資源,或生產(chǎn)者在等待消費(fèi)者消費(fèi)資源
   static final int FULFILLING = 2;
   //棧的頭結(jié)點(diǎn)
   volatile SNode head;

棧節(jié)點(diǎn)

    // 棧節(jié)點(diǎn)
    static final class SNode {
        // 節(jié)點(diǎn)的后繼
        volatile SNode next;
        // 相匹配的節(jié)點(diǎn)
        volatile SNode match;
        // 等待的線程
        volatile Thread waiter;

        // item和mode不需要可見,由于他們總是在其他可見/原子操作寫之前,讀之后
        Object item;// 數(shù)據(jù)
        int mode;//節(jié)點(diǎn)模式

        SNode(Object item) {
            this.item = item;
        }

        // cas保證線程安全設(shè)置節(jié)點(diǎn)后繼節(jié)點(diǎn)
        boolean casNext(SNode cmp, SNode val) {
            return cmp == next && UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
        }

        //嘗試匹配目標(biāo)節(jié)點(diǎn)與本節(jié)點(diǎn),如果匹配,可以喚醒線程。補(bǔ)給者調(diào)用tryMatch方法,確定它們的等待線程。等待線程阻塞到它們自己被匹配。如果匹配返回true
        boolean tryMatch(SNode s) {
            // 設(shè)置本節(jié)點(diǎn)的匹配為s節(jié)點(diǎn)
            if (match == null && UNSAFE.compareAndSwapObject(this, matchOffset, null, s)) {
                Thread w = waiter;
                if (w != null) {
                    waiter = null;
                    LockSupport.unpark(w);
                }
                return true;
            }
            return match == s;
        }

        // 節(jié)點(diǎn)嘗試取消等待,match 從原來的 null 變?yōu)閠his
        void tryCancel() {
            UNSAFE.compareAndSwapObject(this, matchOffset, null, this);
        }

        // match 指向自己,則取消等待
        boolean isCancelled() {
            return match == this;
        }
    }

核心方法

  • isFulfilling:判斷指定類型是否是互補(bǔ)模式
  • casHead(SNode h, SNode nh):替換當(dāng)前頭結(jié)點(diǎn)
  • SNode snode(SNode s, Object e, SNode next, int mode):生成SNode節(jié)點(diǎn)對(duì)象
  • transfer(E e, boolean timed, long nanos): 主要處理邏輯
  • awaitFulfill(SNode s, boolean timed, long nanos): 等待fulfill操作
  • shouldSpin(SNode s):判斷節(jié)點(diǎn)s是頭結(jié)點(diǎn)或是fulfill節(jié)點(diǎn)則返回true
  • clean(SNode s):將head節(jié)點(diǎn)到S節(jié)點(diǎn)之間所有已經(jīng)取消的節(jié)點(diǎn)全部移出
    // 如果m是一個(gè)填充為單元,則返回true
    static boolean isFulfilling(int m) { return (m & FULFILLING) != 0; }
        
    // 比較head是否為h,并且CAS操作nh為當(dāng)前head
    boolean casHead(SNode h, SNode nh) {
        return h == head && UNSAFE.compareAndSwapObject(this, headOffset, h, nh);
    }

    //創(chuàng)建或重新設(shè)置節(jié)點(diǎn)的變量。在節(jié)點(diǎn)入棧時(shí)創(chuàng)建,在當(dāng)可能需要保證減少intervals(間隔)讀和head的CAS操或避免由于競(jìng)爭(zhēng)CAS操作節(jié)點(diǎn)入棧引起的垃圾時(shí),此方法會(huì)被transfer調(diào)用
    static SNode snode(SNode s, Object e, SNode next, int mode) {
        if (s == null) s = new SNode(e);
        s.mode = mode;
        s.next = next;
        return s;
    }

    E transfer(E e, boolean timed, long nanos) {
        // 1.如果隊(duì)列為空或已經(jīng)包含相同模式的節(jié)點(diǎn),則嘗試節(jié)點(diǎn)入棧,等待匹配返回,如果取消返回null。
        // 2.如果包含一個(gè)互補(bǔ)模式的節(jié)點(diǎn)(take(REQUEST)->put(DATA);put(DATA)->take(REQUEST)),則嘗試一個(gè)FULFILLING節(jié)點(diǎn)入棧,同時(shí)匹配等待的協(xié)同節(jié)點(diǎn),兩個(gè)節(jié)點(diǎn)同時(shí)出棧,返回匹配的元素。由于其他線程執(zhí)行步驟3,實(shí)際匹配和解除鏈接指針動(dòng)作不會(huì)發(fā)生。
        // 3.如果棧頂存在另外一個(gè)FULFILLING的節(jié)點(diǎn),則匹配節(jié)點(diǎn),并出棧。這段的代碼與fulfilling相同,除非沒有元素返回
        SNode s = null;
        // 根據(jù)元素判斷節(jié)點(diǎn)模式,元素不為null,則為DATA,否則為REQUEST
        int mode = (e == null) ? REQUEST : DATA;

        for (;;) {
            //剛開始頭節(jié)點(diǎn)為null,第一個(gè)進(jìn)來的節(jié)點(diǎn)就是頭節(jié)點(diǎn)。
            SNode h = head;
            if (h == null || h.mode == mode) {// 如果是空隊(duì)列,或棧頭節(jié)點(diǎn)的模式與要放入的節(jié)點(diǎn)模式相同
                if (timed && nanos <= 0) {
                    //如果超時(shí),則取消等待,出棧,設(shè)置棧頭為其后繼
                    if (h != null && h.isCancelled())
                        casHead(h, h.next);
                    else
                        return null;
                } else if (casHead(h, s = snode(s, e, h, mode))) {
                    //如果非超時(shí),則將創(chuàng)建的新節(jié)點(diǎn)入棧成功,即放在棧頭,自旋等待匹配節(jié)點(diǎn)(timed決定是否超時(shí))
                    SNode m = awaitFulfill(s, timed, nanos);
                    // 返回的m == s 表示該節(jié)點(diǎn)被取消了或者超時(shí)、中斷了
                    if (m == s) {
                        // 如果返回的是自己,節(jié)點(diǎn)取消等待,從棧中移除,并遍歷棧移除取消等待的節(jié)點(diǎn)
                        clean(s);
                        return null;
                    }
                    if ((h = head) != null && h.next == s)
                        //s節(jié)點(diǎn)匹配成功,則設(shè)置棧頭為s的后繼
                        casHead(h, s.next);
                    // 匹配成功,REQUEST模式返回,匹配到的節(jié)點(diǎn)元素(DATA),DATA模式返回當(dāng)前節(jié)點(diǎn)元素
                    return (E) ((mode == REQUEST) ? m.item : s.item);
                }
            } else if (!isFulfilling(h.mode)) { // 如果棧頭節(jié)點(diǎn)模式不為Fulfilling,判斷是否取消等待,是則出棧
                if (h.isCancelled())            // already cancelled
                    casHead(h, h.next);         // pop and retry
                else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) { //非取消等待,則是節(jié)點(diǎn)入棧
                    for (;;) { // 自旋直到節(jié)點(diǎn)匹配或者等待節(jié)點(diǎn)都沒有
                        SNode m = s.next;
                        //后繼節(jié)點(diǎn)為null,則出棧
                        if (m == null) {        // 堆棧中沒有等待節(jié)點(diǎn)
                            casHead(s, null);   // 將棧頭節(jié)點(diǎn)位置設(shè)置為null
                            s = null;           // 棧頭節(jié)點(diǎn)設(shè)置為null,便于GC
                            break;              // 跳出當(dāng)前循環(huán),重新執(zhí)行主循環(huán)
                        }
                        SNode mn = m.next;
                        // 嘗試匹配 s 節(jié)點(diǎn)
                        if (m.tryMatch(s)) {
                            //匹配成功兩個(gè)節(jié)點(diǎn)則出棧
                            casHead(s, mn);     // pop both s and m
                            return (E) ((mode == REQUEST) ? m.item : s.item);
                        } else
                            // 如果沒有匹配成功,則說明已經(jīng)有其它線程與 m 節(jié)點(diǎn)匹配了,將 mn 作為 s 的后繼節(jié)點(diǎn)
                            s.casNext(m, mn);   // help unlink
                    }
                }
            } else {
                //如果棧頭節(jié)點(diǎn)模式為Fulfilling,則代表?xiàng)n^節(jié)點(diǎn)正在與其它節(jié)點(diǎn)匹配,可以理解為協(xié)助棧頭節(jié)點(diǎn)匹配成功
                SNode m = h.next;               // m is h's match
                if (m == null)
                    //如果無后繼節(jié)點(diǎn),則棧頭出棧
                    casHead(h, null);           // pop fulfilling node
                else {
                    //嘗試匹配,如果匹配成功,棧頭和匹配節(jié)點(diǎn)出棧,否則跳過后繼節(jié)點(diǎn)
                    SNode mn = m.next;
                    if (m.tryMatch(h))          // help match
                        casHead(h, mn);         // pop both h and m
                    else                        // lost match
                        h.casNext(m, mn);       // help unlink
                }
            }
        }
    }

    // 自旋或阻塞,直到節(jié)點(diǎn)被一個(gè)fulfill操作匹配
    SNode awaitFulfill(SNode s, boolean timed, long nanos) {
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        Thread w = Thread.currentThread();
        //獲取自旋的次數(shù)
        int spins = (shouldSpin(s) ? (timed ? maxTimedSpins : maxUntimedSpins) : 0);
        for (;;) {
            // 如果線程被中斷,則取消等待
            if (w.isInterrupted())
                s.tryCancel();
            SNode m = s.match;
            // 如果節(jié)點(diǎn)的匹配節(jié)點(diǎn)不為null,則返回匹配節(jié)點(diǎn)
            if (m != null)
                return m;
            if (timed) {
                nanos = deadline - System.nanoTime();
                //如果超時(shí),則取消等待
                if (nanos <= 0L) {
                    s.tryCancel();
                    continue;
                }
            }
            // 如果自旋次數(shù)大于零,且可以自旋,則自旋次數(shù)減1
            if (spins > 0)
                spins = shouldSpin(s) ? (spins-1) : 0;
            else if (s.waiter == null)
                //如果節(jié)點(diǎn)S的等待線程為空,則設(shè)置當(dāng)前節(jié)點(diǎn)為S節(jié)點(diǎn)的等待線程,以便可以park后繼節(jié)點(diǎn)。
                s.waiter = w; // establish waiter so can park next iter
            else if (!timed)
                //非超時(shí)等在者,park當(dāng)前線程
                LockSupport.park(this);
            else if (nanos > spinForTimeoutThreshold)
                //如果超時(shí)時(shí)間大于,最大自旋閾值,則超時(shí)park當(dāng)前線程
                LockSupport.parkNanos(this, nanos);
        }
    }

    // 如果節(jié)點(diǎn)在棧頭或棧頭為FULFILLING的節(jié)點(diǎn),則返回true
    boolean shouldSpin(SNode s) {
        //因?yàn)楹芸赡芰⒖叹蜁?huì)有新的線程到來,那么就會(huì)立刻進(jìn)行交易而不需要進(jìn)行阻塞,然后被喚醒,這是需要過程的,所以這樣的自旋等待是值得的。
        SNode h = head;
        return (h == s || h == null || isFulfilling(h.mode));
    }

    // 將head節(jié)點(diǎn)到S節(jié)點(diǎn)之間所有已經(jīng)取消的節(jié)點(diǎn)全部移出。
    void clean(SNode s) {
        s.item = null;   // forget item
        s.waiter = null; // forget thread

        SNode past = s.next;
        if (past != null && past.isCancelled())
            past = past.next;

        // 如果取消的是頭節(jié)點(diǎn)則運(yùn)行下面的清理操作,操作邏輯很簡(jiǎn)單就是判斷頭結(jié)點(diǎn)是不是取消節(jié)點(diǎn),如果是則將節(jié)點(diǎn)一定到下一個(gè)節(jié)點(diǎn)
        SNode p;
        while ((p = head) != null && p != past && p.isCancelled())
            //p是從頭節(jié)點(diǎn)開始第一個(gè)不移除的節(jié)點(diǎn)
            casHead(p, p.next);

        // 取消不是頭結(jié)點(diǎn)的嵌套節(jié)點(diǎn)
        while (p != null && p != past) {
            SNode n = p.next;
            if (n != null && n.isCancelled())
                //移除節(jié)點(diǎn)n
                p.casNext(n, n.next);
            else
                p = n;
        }
    }

因?yàn)槎褩5某鰲:腿霔2僮鞫荚?transfer 方法里面,所以不容易理解。建議讀者多看幾遍,結(jié)合下面的圖和我個(gè)人分析的思路,一步一步的debug,這樣理解起來應(yīng)該就不難啦~

1.根據(jù)節(jié)點(diǎn)模式判斷是入棧(put)還是出棧(take)操作
2.判斷棧頭是否為空或棧頭節(jié)點(diǎn)操作是否和本次一樣,是的話執(zhí)行第3步,否則執(zhí)行第6步
3.判斷是否是超時(shí)操作,如果是超時(shí)操作的話則執(zhí)行第4步,否則執(zhí)行第5步
4.判斷棧頭是否非空并且是否可以取消,是的話將棧頭后繼節(jié)點(diǎn)cas操作成為棧頭節(jié)點(diǎn)后執(zhí)行第1步,否則返回null
5.cas操作創(chuàng)建節(jié)點(diǎn)并將該節(jié)點(diǎn)入棧,自旋等待匹配節(jié)點(diǎn)
6.判斷棧頭節(jié)點(diǎn)模式是否為Fulfilling,如果不是的話執(zhí)行第7步,否則執(zhí)行第10步
7.判斷棧頭節(jié)點(diǎn)是否需要取消等待,需要取消等待的話將棧頭節(jié)點(diǎn)的后繼節(jié)點(diǎn)cas操作成為頭節(jié)點(diǎn)后重新執(zhí)行第1步,否則執(zhí)行第8步
8.棧頭節(jié)點(diǎn)不需取消等待,將當(dāng)前(take or put or poll)操作封裝為一個(gè)節(jié)點(diǎn)入棧后自旋堆棧,直到棧頭節(jié)點(diǎn)與棧中其它節(jié)點(diǎn)匹配后兩個(gè)節(jié)點(diǎn)都出棧返回節(jié)點(diǎn)信息或者所有的等待節(jié)點(diǎn)都沒有后跳出子循環(huán)重新執(zhí)行第1步
9.棧頭節(jié)點(diǎn)模式為Fulfilling,如果棧頭節(jié)點(diǎn)的后繼節(jié)點(diǎn)為null,cas設(shè)置棧頭節(jié)點(diǎn)為null并執(zhí)行第1步,否則繼續(xù)嘗試匹配棧中其它節(jié)點(diǎn)

2.SynchronousQueue 的公平模式(TransferQueue)

公平模式下使用的數(shù)據(jù)結(jié)構(gòu)是隊(duì)列,其方式是先進(jìn)先出(FIFO:First In First Out)。就比如說咱們?cè)诮Y(jié)賬排隊(duì)的時(shí)候,肯定是先排隊(duì)的人先結(jié)賬呀,這樣才公平!

隊(duì)列節(jié)點(diǎn)

// 隊(duì)列節(jié)點(diǎn)
static final class QNode {
    // 下一個(gè)節(jié)點(diǎn)
    volatile QNode next;
    // 元素信息
    volatile Object item;
    // 當(dāng)前等待的線程
    volatile Thread waiter;
    // 是否是數(shù)據(jù)(put的時(shí)候是true,take的時(shí)候是false)
    final boolean isData;

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

    // 替換當(dāng)前節(jié)點(diǎn)的next節(jié)點(diǎn)
    boolean casNext(QNode cmp, QNode val) {
        return next == cmp &&
                UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
    }

    // 替換當(dāng)前節(jié)點(diǎn)的item數(shù)據(jù)
    boolean casItem(Object cmp, Object val) {
        return item == cmp &&
                UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
    }

    // 取消當(dāng)前操作,將當(dāng)前item賦值為this(當(dāng)前QNode節(jié)點(diǎn))
    void tryCancel(Object cmp) {
        UNSAFE.compareAndSwapObject(this, itemOffset, cmp, this);
    }

    // 如果item是this(當(dāng)前QNode節(jié)點(diǎn))的話就返回true,反之返回false
    boolean isCancelled() {
        return item == this;
    }

    // 如果已知此節(jié)點(diǎn)離隊(duì)列,判斷next節(jié)點(diǎn)是不是為this,則返回true
    boolean isOffList() {
        return next == this;
    }
}

成員變量

// 隊(duì)列頭節(jié)點(diǎn)
transient volatile QNode head;
// 隊(duì)列尾節(jié)點(diǎn)
transient volatile QNode tail;
// 節(jié)點(diǎn)被取消但沒有從隊(duì)列中移除
transient volatile QNode cleanMe;

核心方法

  • advanceHead(QNode h, QNode nh):更新頭節(jié)點(diǎn)
  • advanceTail(QNode t, QNode nt):更新尾節(jié)點(diǎn)
  • casCleanMe(QNode cmp, QNode val):更新 cleanMe 節(jié)點(diǎn)
  • awaitFulfill(QNode s, E e, boolean timed, long nanos):等待fulfill操作
  • clean(QNode pred, QNode s):清空cleanMe節(jié)點(diǎn)
  • transfer(E e, boolean timed, long nanos): 主要處理邏輯
    void advanceHead(QNode h, QNode nh) {
        if (h == head && UNSAFE.compareAndSwapObject(this, headOffset, h, nh))
            h.next = h; // forget old next
    }
    
    void advanceTail(QNode t, QNode nt) {
        if (tail == t)
            UNSAFE.compareAndSwapObject(this, tailOffset, t, nt);
    }
    
    boolean casCleanMe(QNode cmp, QNode val) {
        return cleanMe == cmp && UNSAFE.compareAndSwapObject(this, cleanMeOffset, cmp, val);
    }
    
    Object awaitFulfill(QNode s, E e, boolean timed, long nanos) {
        // 和 TransferStack.awaitFulfill 方法的邏輯一樣,因此就不顯示方法的邏輯啦
    }
    
    void clean(QNode pred, QNode s) {
        s.waiter = null; // forget thread
        while (pred.next == s) { // Return early if already unlinked
            QNode h = head;
            QNode hn = h.next;   // Absorb cancelled first node as head
            if (hn != null && hn.isCancelled()) {
                advanceHead(h, hn);
                continue;
            }
            QNode t = tail;      // Ensure consistent read for tail
            if (t == h)
                return;
            QNode tn = t.next;
            // 判斷現(xiàn)在的t是不是末尾節(jié)點(diǎn),可能其他線程插入了內(nèi)容導(dǎo)致不是最后的節(jié)點(diǎn)。
            if (t != tail)
                continue;
            // 如果不是最后節(jié)點(diǎn)的話將其現(xiàn)在t.next節(jié)點(diǎn)作為tail尾節(jié)點(diǎn)。
            if (tn != null) {
                advanceTail(t, tn);
                continue;
            }
            // 如果當(dāng)前節(jié)點(diǎn)不是尾節(jié)點(diǎn)進(jìn)入到這里面。
            if (s != t) {        // If not tail, try to unsplice
                // 獲取當(dāng)前節(jié)點(diǎn)(被取消的節(jié)點(diǎn))的下一個(gè)節(jié)點(diǎn)。
                QNode sn = s.next;
                // 修改上一個(gè)節(jié)點(diǎn)的next(下一個(gè))元素為下下個(gè)節(jié)點(diǎn)。
                if (sn == s || pred.casNext(s, sn))
                    return;
            }
            QNode dp = cleanMe;
            // 嘗試清除上一個(gè)標(biāo)記為清除的節(jié)點(diǎn)
            if (dp != null) {    // Try unlinking previous cancelled node
                //1.獲取要被清除的節(jié)點(diǎn)
                QNode d = dp.next;
                QNode dn;
                if (d == null ||               // d is gone or
                        d == dp ||                 // d is off list or
                        !d.isCancelled() ||        // d not cancelled or
                        (d != t &&                 // d not tail and
                                (dn = d.next) != null &&  //   has successor
                                dn != d &&                //   that is on list
                                dp.casNext(d, dn)))       // d unspliced
                    casCleanMe(dp, null);
                if (dp == pred)
                    return;      // s is already saved node
            } else if (casCleanMe(null, pred))
                return;          // Postpone cleaning s
        }
    }   
    
    E transfer(E e, boolean timed, long nanos) {
        QNode s = null; // constructed/reused as needed
        // 標(biāo)識(shí)此次操作是存數(shù)據(jù)(put)還是取數(shù)據(jù)(take)
        boolean isData = (e != null);

        // 自旋匹配節(jié)點(diǎn)
        for (;;) {
            QNode t = tail;
            QNode h = head;
            // 如果頭節(jié)點(diǎn)或尾節(jié)點(diǎn)為空繼續(xù)自旋(在TransferQueue初始化的時(shí)候已經(jīng)賦值頭尾結(jié)點(diǎn))
            if (t == null || h == null)         // saw uninitialized value
                continue;                       // spin

            // h == t 說明頭尾結(jié)點(diǎn)相同,是空隊(duì)列
            // t.isData == isData 說明尾節(jié)點(diǎn)與當(dāng)前操作一樣
            if (h == t || t.isData == isData) { // empty or same-mode
                QNode tn = t.next;
                // 如果臨時(shí)變量 t 不等于尾節(jié)點(diǎn),說明有其它線程改變了尾節(jié)點(diǎn),則重新自旋匹配節(jié)點(diǎn)
                if (t != tail)                  // inconsistent read
                    continue;
                // 如果尾節(jié)點(diǎn)之后的節(jié)點(diǎn)值不為空,說明也是有其它線程改變了尾節(jié)點(diǎn),將tn節(jié)點(diǎn)賦值給尾節(jié)點(diǎn)
                if (tn != null) {               // lagging tail
                    advanceTail(t, tn);
                    continue;
                }
                //超時(shí)直接返回 null
                if (timed && nanos <= 0)
                    return null;
                // 創(chuàng)建node節(jié)點(diǎn)
                if (s == null)
                    s = new QNode(e, isData);
                // 將新創(chuàng)建的node節(jié)點(diǎn)添加到隊(duì)列尾部,如果失敗則重新自旋
                if (!t.casNext(null, s))
                    continue;
                
                // 更新新創(chuàng)建節(jié)點(diǎn)為尾節(jié)點(diǎn)
                advanceTail(t, s);            
                // 調(diào)用 awaitFulfill 方法自旋匹配等待節(jié)點(diǎn)
                Object x = awaitFulfill(s, e, timed, nanos);
                // 如果返回當(dāng)前節(jié)點(diǎn),則說明節(jié)點(diǎn)由于被取消、超時(shí)、中斷導(dǎo)致匹配失敗
                if (x == s) {                   // wait was cancelled
                    // 清除當(dāng)前等待匹配節(jié)點(diǎn)
                    clean(t, s);
                    return null;
                }

                // 判斷節(jié)點(diǎn)是否已從隊(duì)列離開
                if (!s.isOffList()) {           // not already unlinked
                    // 嘗試將s節(jié)點(diǎn)設(shè)置為head,移出t
                    advanceHead(t, s);          // unlink if head
                    if (x != null)              // and forget fields
                        s.item = s;
                    // 釋放 s 節(jié)點(diǎn)當(dāng)前的等待線程
                    s.waiter = null;
                }
                // 返回節(jié)點(diǎn)值(put返回put操作的值,take返回匹配到的節(jié)點(diǎn)值)
                return (x != null) ? (E)x : e;
            } else {// 隊(duì)列不為空,并且當(dāng)前操作與尾節(jié)點(diǎn)的操作不一致。所以當(dāng)前操作與尾節(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 == (x != null):判斷isData與x的模式是否相同,相同表示已經(jīng)完成匹配,繼續(xù)自旋
                // x == m :m節(jié)點(diǎn)被取消了
                // !m.casItem(x, e):如果嘗試將數(shù)據(jù)e設(shè)置到m上失敗
                if (isData == (x != null) ||    // m already fulfilled
                        x == m ||                   // m cancelled
                        !m.casItem(x, e)) {         // lost CAS
                    // 將m設(shè)置為頭結(jié)點(diǎn),h出列,然后重試
                    advanceHead(h, m);          // dequeue and retry
                    continue;
                }

                // 成功匹配了,節(jié)點(diǎn)m 設(shè)置為頭結(jié)點(diǎn),h出列
                advanceHead(h, m);              // successfully fulfilled
                // 喚醒節(jié)點(diǎn) m 的等待線程
                LockSupport.unpark(m.waiter);
                // 返回節(jié)點(diǎn)值(put返回put操作的值,take返回匹配到的節(jié)點(diǎn)值)
                return (x != null) ? (E)x : e;
            }
        }
    }        

OMG!源碼又是那么長(zhǎng)。但其實(shí)也不是很難,知道一個(gè)規(guī)律就行:由于是使用隊(duì)列作為公平策略,所以在put的時(shí)候會(huì)在隊(duì)列尾部添加數(shù)據(jù),而take的時(shí)候會(huì)從隊(duì)列尾部向隊(duì)列頭部方向?qū)ふ业谝粋€(gè)被阻塞的線程,這樣就可以保證公平的、按順序的釋放被阻塞的線程。

先簡(jiǎn)單的總結(jié)一下核心流程(TransferQueue.trasnfer方法):
1.獲取當(dāng)前操作是存數(shù)據(jù)還是取數(shù)據(jù)
2.自旋尋找匹配的節(jié)點(diǎn)(put操作匹配take操作、take操作匹配put操作)
3.如果頭節(jié)點(diǎn)或尾節(jié)點(diǎn)為空,則繼續(xù)執(zhí)行第2步
4.如果是空隊(duì)列或尾節(jié)點(diǎn)和當(dāng)前操作一樣,執(zhí)行第5步,否則執(zhí)行第13步
5.如果尾節(jié)點(diǎn)被其它線程更改,重新執(zhí)行第2步,否則執(zhí)行第6步
6.如果尾節(jié)點(diǎn)的后繼節(jié)點(diǎn)不為空,說明有其它線程更改,則設(shè)置后繼節(jié)點(diǎn)為尾節(jié)點(diǎn),并重新執(zhí)行第2步;否則執(zhí)行第7步
7.如果超時(shí)直接返回null,否則往下執(zhí)行第8步
8.為當(dāng)前操作創(chuàng)建節(jié)點(diǎn)并添加到隊(duì)列尾部,如果添加成功往下執(zhí)行第9步,否則執(zhí)行第2步
9.自旋匹配等待節(jié)點(diǎn),當(dāng)返回節(jié)點(diǎn)與當(dāng)前節(jié)點(diǎn)不一樣,說明節(jié)點(diǎn)匹配成功,執(zhí)行第10步;否則說明由于被取消、超時(shí)、中斷導(dǎo)致匹配失敗,則清除當(dāng)前節(jié)點(diǎn)并返回null
10.如果節(jié)點(diǎn)已從隊(duì)列離開,執(zhí)行第11步,否則執(zhí)行第12步
11.返回節(jié)點(diǎn)值(put返回put操作的值,take返回匹配到的節(jié)點(diǎn)值)
12.將節(jié)點(diǎn)從隊(duì)列中移除,并重新設(shè)置隊(duì)列頭節(jié)點(diǎn)后執(zhí)行第11步
13.執(zhí)行到這一步,說明當(dāng)前操作與尾節(jié)點(diǎn)的操作是互相匹配;那么如果隊(duì)列頭節(jié)點(diǎn)是否匹配完成或隊(duì)列頭節(jié)點(diǎn)被取消,又或者cas更新頭節(jié)點(diǎn)操作失敗,則執(zhí)行第14步,否則執(zhí)行第15步
14.重新設(shè)置頭節(jié)點(diǎn)并執(zhí)行第2步
15.執(zhí)行到這一步代表匹配成功,重新設(shè)置隊(duì)列的頭節(jié)點(diǎn),并喚醒頭節(jié)點(diǎn)的等待線程,最后返回節(jié)點(diǎn)值(put返回put操作的值,take返回匹配到的節(jié)點(diǎn)值)

還是依照個(gè)人習(xí)慣,喜歡通過畫圖分析一下源碼流程!


總結(jié):

  • SynchronousQueue 是一個(gè)沒有隊(duì)列大小的概念,所有的操作都必須與其匹配的節(jié)點(diǎn)共同入隊(duì)出隊(duì)(公平模式)或入棧出棧(非公平模式)
  • SynchronousQueue 是輕量級(jí)的阻塞隊(duì)列。因?yàn)镾ynchronousQueue是沒有使用到鎖,都是通過CAS方法保證線程安全

其實(shí)也不難發(fā)現(xiàn),SynchronousQueue 的缺點(diǎn)也是十分明顯。如果同一個(gè)模式的節(jié)點(diǎn)多的話,就會(huì)一直阻塞,這是會(huì)損耗性能,所以需要根據(jù)實(shí)際業(yè)務(wù)場(chǎng)景使用。

最后希望讀者們看源碼的時(shí)候,親自debug,這樣才會(huì)加深源碼的理解,讀任何文章都只是輔助,自己真正理解才是學(xué)會(huì)東西。

如果覺得源碼剖析不錯(cuò)的話,麻煩點(diǎn)個(gè)贊哈!對(duì)于文章有哪里不清楚或者有誤的地方,歡迎在評(píng)論區(qū)留言~

參考資料:

https://www.cnblogs.com/dwlsxj/p/Thread.html
慕課網(wǎng):面試官系統(tǒng)精講Java源碼及大廠真題: https://www.imooc.com/read/47/article/862

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

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