阻塞佇列 SynchronousQueue 原始碼解析

槑!發表於2020-10-16

SynchronousQueue

  • 特性
    • SynchronousQueue 沒有容量。與其他 BlockingQueue 不同,SynchronousQueue 是一個不儲存元素的 BlockingQueue。可以有多個 put ,但每一個put操作必須要等待一個take操作,否則該執行緒會一直阻塞在 put,反之亦然
    • 因為沒有容量,所以對應 peek, contains, clear, isEmpty … 等方法其實是無效的。例如 clear 是不執行任何操作的,contains 始終返回 false,peek 始終返回 null。
    • SynchronousQueue 分為公平和非公平,預設情況下采用非公平性訪問策略,當然也可以通過建構函式來設定為公平性訪問策略(為true即可)。
  • 底層
    • 每個節點都有自己的模式,用來判斷是請求節點(出隊請求)還是資料節點(入隊請求),如果隊首節點的模式和這次 transfer 的模式不同,那就匹配上了,那麼就可以讓隊首出隊並喚醒阻塞在隊首上的執行緒,如果不匹配那麼就如入隊等待。注意正是因為這種特性,佇列中等待的節點永遠可以保證是同一模式。
      • 公平:用佇列實現,如果是同種模式要入隊阻塞,那麼就放到隊尾(casNext)
      • 非公平:用棧實現,如果是同種模式要入棧阻塞,因為棧的特性,是放到第一個位置(casHead),所以後入棧,但先出棧匹配
    • 併發安全是通過 CAS 保證併發安全,入隊等待(模式相同,自旋 + casNext ,失敗則從迴圈開始處重新執行)和節點匹配(模式不同,自旋 + casItem,節點的 item 屬性是兩個執行緒資料交換的場地,一旦併發競爭這個節點被其他執行緒搶先匹配成功,item 就會 cas 失敗,那麼就也是從迴圈開始處再來,下次不一定還會模式不同,因為一旦 cas 成功會讓被匹配的節點出隊,所以不存在下次迴圈還匹配這次失敗的節點)。阻塞是通過 LockSupport
Transferer
定義
abstract static class Transferer<E> {
    // 如果e != null,相當於將一個資料交給消費者
    // 如果e == null,則相當於從一個生產者接收一個消費者交出的資料。
    abstract E transfer(E e, boolean timed, long nanos);
}
TransferQueue

TransferQueue是實現公平性策略的核心類,其節點為QNode

static final class TransferQueue<E> extends Transferer<E> {
    /** 頭節點 */
    transient volatile QNode head;
    /** 尾節點 */
    transient volatile QNode tail;
    // 指向一個取消的結點
    //當一個節點中最後一個插入時,它被取消了但是可能還沒有離開佇列
    transient volatile QNode cleanMe;
    
    
    TransferQueue() {
    	QNode h = new QNode(null, false); // initialize to dummy node.
   	 	head = h;
    	tail = h;
	}
    
    
    static final class QNode {
        // next 域
        volatile QNode next;
        // item資料項
        volatile Object item;
        //  等待執行緒,用於park/unpark
        volatile Thread waiter;      
        //模式,表示當前是資料還是請求,只有當匹配的模式相匹配時才會交換
        final boolean isData;

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

        // CAS next域,在TransferQueue中用於向next推進
        boolean casNext(QNode cmp, QNode val) {
            return next == cmp &&
                    UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
        }

        // CAS itme資料項
        boolean casItem(Object cmp, Object val) {
            return item == cmp &&
                    UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
        }

        // 取消本結點,將item域設定為自身作為標誌
        void tryCancel(Object cmp) {
            UNSAFE.compareAndSwapObject(this, itemOffset, cmp, this);
        }

        // 是否被取消,與tryCancel相照應只需要判斷item釋放等於自身即可
        boolean isCancelled() {
            return item == this;
        }


        boolean isOffList() {
            return next == this;
        }

        private static final sun.misc.Unsafe UNSAFE;
        private static final long itemOffset;
        private static final long nextOffset;

        static {
            try {
                UNSAFE = sun.misc.Unsafe.getUnsafe();
                Class<?> k = QNode.class;
                itemOffset = UNSAFE.objectFieldOffset
                        (k.getDeclaredField("item"));
                nextOffset = UNSAFE.objectFieldOffset
                        (k.getDeclaredField("next"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }
	}

    /**
     * 省略很多程式碼
     */
}
TransferStack

TransferStack用於實現非公平性

static final class TransferStack<E> extends Transferer<E> {

    // 三種 SNode 的 mode
    // REQUEST表示消費資料的消費者
    static final int REQUEST    = 0;
	// DATA表示生產資料的生產者
    static final int DATA       = 1;
	// FULFILLING,表示匹配另一個生產者或消費者
    static final int FULFILLING = 2;

    volatile SNode head;
    
    static final class SNode {
        // next 域
        volatile SNode next;
        // 相匹配的節點
        volatile SNode match;
        // 等待的執行緒
        volatile Thread waiter;
        // item 域
        Object item;                // data; or null for REQUESTs
        // 模式
        int mode;

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

        boolean casNext(SNode cmp, SNode val) {
            return cmp == next && UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); 
        }

        // 將 s 結點設定成匹配的節點,若匹配成功,則unpark等待執行緒
        boolean tryMatch(SNode s) {
            if (match == null &&
                    UNSAFE.compareAndSwapObject(this, matchOffset, null, s)) {
                Thread w = waiter;
                if (w != null) {    // waiters need at most one unpark
                    waiter = null;
                    LockSupport.unpark(w);
                }
                return true;
            }
            return match == s;
        }

        // cancle 是把 match 的物件設定自己
        void tryCancel() { UNSAFE.compareAndSwapObject(this, matchOffset, null, this); }

        boolean isCancelled() { return match == this; }

        // Unsafe mechanics
        private static final sun.misc.Unsafe UNSAFE;
        private static final long matchOffset;
        private static final long nextOffset;

        static {
            try {
                UNSAFE = sun.misc.Unsafe.getUnsafe();
                Class<?> k = SNode.class;
                matchOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("match"));
                nextOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("next"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }
	}

    /**
     * 省略一堆程式碼
     */

}
構造方法
public class SynchronousQueue<E> extends AbstractQueue<E>
    implements BlockingQueue<E>, java.io.Serializable{
    
	public SynchronousQueue() {
        this(false);
    }

    public SynchronousQueue(boolean fair) {
        // 通過 fair 值來決定公平性和非公平性
        // 公平性使用TransferQueue,非公平性採用TransferStack
        transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
    }    
    
    //...
}

put & get

都是直接呼叫的 transfer

// 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();
    }
}

// take操作
public E take() throws InterruptedException {
    E e = transferer.transfer(null, false, 0);
    if (e != null)
        return e;
    Thread.interrupted();
    throw new InterruptedException();
}
TransferQueue
E transfer(E e, boolean timed, long nanos) {
    QNode s = null;
    // 當前請求模式
    boolean isData = (e != null);

    for (;;) {
        QNode t = tail;
        QNode h = head;
        // 頭、尾節點 為null,沒有初始化
        if (t == null || h == null)
            continue;

        // 頭尾節點相等(佇列為null) 或者當前節點和佇列節點模式一樣,那麼就要入隊等待了
        if (h == t || t.isData == isData) {
            // tn = t.next
            QNode tn = t.next;
            // t != tail表示已有其他執行緒操作了,修改了tail,重新再來
            if (t != tail)
                continue;
            
            // tn != null,表示已經有其他執行緒新增了節點,tn 推進,重新處理
            if (tn != null) {
                // 當前執行緒幫忙推進尾節點,就是嘗試將tn設定為尾節點
                advanceTail(t, tn);
                continue;
            }
            
            //  呼叫的方法的 wait 型別的, 並且 超時了, 直接返回 null
            // timed 在take操作闡述
            if (timed && nanos <= 0)
                return null;

            // s == null,構建一個新節點Node
            if (s == null)
                s = new QNode(e, isData);

            // 將新建的節點加入到佇列中,如果插入失敗,則重新自旋
            if (!t.casNext(null, s))
                continue;

            // 替換尾節點(不成功無所謂的,因為連線時會找到真正的尾節點,這個只是一個推進作用)
            advanceTail(t, s);

            // 呼叫 awaitFulfill, 進行阻塞,並等待返回結果
            Object x = awaitFulfill(s, e, timed, nanos);

            // 若返回的 x == s,表示當前執行緒已經超時或者中斷,不然的話s == null 或者 是匹配的節點
            if (x == s) {
                // 清理節點S
                clean(t, s);
                return null;
            }

            // isOffList:用於判斷節點是否已經從佇列中離開了
            if (!s.isOffList()) {
                // 嘗試將S節點設定為head,移出t
                advanceHead(t, s);
                if (x != null)
                    s.item = s;
                // 釋放執行緒 ref
                s.waiter = null;
            }

            // 返回
            return (x != null) ? (E)x : e;

        }

        // 這裡是就說明這個請求的模式 和佇列裡節點的模式不同,那麼就可以匹配了
        else {
            // 第一個元素節點(head 指向的節點並不是第一個元素節點,可能是初始化節點或者已經匹配完了的節點)
            QNode m = h.next;

            // 不一致讀,重新開始
            // 有其他執行緒更改了執行緒結構
            if (t != tail || m == null || h != head)
                continue;

            /**
             * 下面是生產者producer和消費者consumer匹配操作
             */
            Object x = m.item;
            // isData == (x != null):判斷isData與x的模式是否相同,相同表示已經匹配了
            // x == m :m節點被取消了(取消的標誌)
            // !m.casItem(x, e):如果嘗試將資料e設定到m上失敗,說明這個節點被其他執行緒匹配過了
            if (isData == (x != null) ||  x  == m || !m.casItem(x, e)) {
                // 將m設定為頭結點,h出列,再從頭開始
                advanceHead(h, m);
                continue;
            }

            // 成功匹配了,m 設定為頭結點h出列,向前推進
            advanceHead(h, m);
            // 喚醒 m 上的等待執行緒
            LockSupport.unpark(m.waiter);
            return (x != null) ? (E)x : e;
        }
    }
}

Object awaitFulfill(QNode s, E e, boolean timed, long nanos) {

    // 超時控制
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    Thread w = Thread.currentThread();
    // 自旋次數
    // 如果節點Node恰好是第一個資料節點,則自旋一段時間,這裡主要是為了效率問題,如果裡面阻塞,會存在喚醒、執行緒上下文切換的問題
    // 如果生產者、消費者者裡面到來的話,就避免了這個阻塞的過程
    int spins = ((head.next == s) ?
            (timed ? maxTimedSpins : maxUntimedSpins) : 0);
    
    // 自旋
    for (;;) {
        // 執行緒中斷了,剔除當前節點
        if (w.isInterrupted())
            s.tryCancel(e);

        // 如果被其他執行緒匹配了,直接返回當前 item 即可
        // 實際上 item 就是 put 、get 進行資料交換的地方
        Object x = s.item;
        if (x != e)
            return x;
        
        // 超時判斷
        if (timed) {
            nanos = deadline - System.nanoTime();
            // 如果超時了,取消節點,continue,在if(x != e)肯定會成立,直接返回x
            if (nanos <= 0L) {
                s.tryCancel(e);
                continue;
            }
        }

        // 自旋- 1
        if (spins > 0)
            --spins;

        // 等待執行緒
        else if (s.waiter == null)
            s.waiter = w;

        // 進行沒有超時的 park
        else if (!timed)
            LockSupport.park(this);

        // 自旋次數過了, 直接 + timeout 方式 park
        else if (nanos > spinForTimeoutThreshold)
            LockSupport.parkNanos(this, nanos);
    }
}
TransferStack

和 TransferQueue 區別不大,只是變成了每次模式相同入隊是從頭入

E transfer(E e, boolean timed, long nanos) {
    SNode s = null; // constructed/reused as needed
    int mode = (e == null) ? REQUEST : DATA;

    for (;;) {
        SNode h = head;
        // 棧為空 或者 當前節點模式與頭節點模式一樣,將節點壓入棧內,等待匹配
        if (h == null || h.mode == mode) {
            // 超時
            if (timed && nanos <= 0) {
                // 節點被取消了,向前推進
                if (h != null && h.isCancelled())
                    //  重新設定頭結點(彈出之前的頭結點)
                    casHead(h, h.next);
                else
                    return null;
            }
            // 不超時
            // 生成一個SNode節點,並嘗試替換掉頭節點head (head -> s)
            // 頭插失敗的話還會重新自旋
            else if (casHead(h, s = snode(s, e, h, mode))) {
                // 自旋,等待匹配成功的節點喚醒
                SNode m = awaitFulfill(s, timed, nanos);
                // 返回的m == s 表示該節點被取消了或者超時、中斷了
                if (m == s) {
                    // 清理節點S,return null
                    clean(s);
                    return null;
                }

                // 因為通過前面一步頭插成功 s 成了 head ,如果h.next == s ,則表示有其他節點插入到 s 前面了,
                // 且該節點就是與節點 s 匹配的節點
                if ((h = head) != null && h.next == s)
                    // 將s.next節點設定為head,相當於取消節點h、s
                    casHead(h, s.next);

                // 如果是請求則返回匹配的域,否則返回節點 S 的域
                return (E) ((mode == REQUEST) ? m.item : s.item);
            }
        }

        // 如果棧不為null,且兩者模式不匹配(h != null && h.mode != mode)
        // 說明他們是一隊對等匹配的節點,嘗試用當前節點s來滿足h節點
        else if (!isFulfilling(h.mode)) {
            // head 節點已經取消了,向前推進
            if (h.isCancelled())
                casHead(h, h.next);

            // 嘗試將當前節點打上"正在匹配"的標記,並設定為 head
            else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) {
                // 迴圈loop
                for (;;) {
                    // s為當前節點,m是s的next節點,
                    // m節點是s節點的匹配節點
                    SNode m = s.next;
                    // m == null,其他節點把m節點匹配走了
                    if (m == null) {
                        // 將s彈出
                        casHead(s, null);
                        // 將s置空,下輪迴圈的時候還會新建
                        s = null;
                        // 退出該迴圈,繼續主迴圈
                        break;
                    }
                    // 獲取m的next節點
                    SNode mn = m.next;
                    // 嘗試匹配
                    if (m.tryMatch(s)) {
                        // 匹配成功,將s 、 m彈出
                        casHead(s, mn);     // pop both s and m
                        return (E) ((mode == REQUEST) ? m.item : s.item);
                    } else
                        // 如果沒有匹配成功,說明有其他執行緒已經匹配了,把m移出
                        s.casNext(m, mn);
                }
            }
        }
        // 到這最後一步說明節點正在匹配階段
        else {
            // head 的next的節點,是正在匹配的節點,m 和 h配對
            SNode m = h.next;

            // m == null 其他執行緒把m節點搶走了,彈出h節點
            if (m == null)
                casHead(h, null);
            else {
                SNode mn = m.next;
                if (m.tryMatch(h))
                    casHead(h, mn);
                else
                    h.casNext(m, mn);
            }
        }
    }
}

SNode awaitFulfill(SNode s, boolean timed, long nanos) {
    // 超時
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    // 當前執行緒
    Thread w = Thread.currentThread();

    // 自旋次數
    // shouldSpin 用於檢測當前節點是否需要自旋
    // 如果棧為空、該節點是首節點或者該節點是匹配節點,則先採用自旋,否則阻塞
    int spins = (shouldSpin(s) ?
            (timed ? maxTimedSpins : maxUntimedSpins) : 0);
    
    for (;;) {
        // 執行緒中斷了,取消該節點
        if (w.isInterrupted())
            s.tryCancel();

        // 匹配節點
        SNode m = s.match;

        // 如果匹配節點m不為空,則表示匹配成功,直接返回
        if (m != null)
            return m;
        // 超時
        if (timed) {
            nanos = deadline - System.nanoTime();
            // 節點超時,取消
            if (nanos <= 0L) {
                s.tryCancel();
                continue;
            }
        }

        // 自旋;每次自旋的時候都需要檢查自身是否滿足自旋條件,滿足就 - 1,否則為0
        if (spins > 0)
            spins = shouldSpin(s) ? (spins-1) : 0;

        // 第一次阻塞時,會將當前執行緒設定到s上
        else if (s.waiter == null)
            s.waiter = w;

        // 阻塞 當前執行緒
        else if (!timed)
            LockSupport.park(this);
        
        // 超時
        else if (nanos > spinForTimeoutThreshold)
            LockSupport.parkNanos(this, nanos);
    }
}

相關文章