java源碼學習7-SynchronousQueue

一、SynchronousQueue API介紹

public class SynchronousQueue<E>extends AbstractQueue<E>implements BlockingQueue<E>, Serializable

一種阻塞隊列,其中每個 put 必須等待一個 take,反之亦然。同步隊列沒有任何內部容量,甚至連一個隊列的容量都沒有。不能在同步隊列上進行 peek,因爲僅在試圖要取得元素時,該元素才存在;除非另一個線程試圖移除某個元素,否則也不能(使用任何方法)添加元素;也不能迭代隊列,因爲其中沒有元素可用於迭代。隊列的頭 是嘗試添加到隊列中的首個已排隊線程元素;如果沒有已排隊線程,則不添加元素並且頭爲 null。對於其他 Collection 方法(例如 contains),SynchronousQueue 作爲一個空集合。此隊列不允許 null 元素。

同步隊列類似於 CSP 和 Ada 中使用的 rendezvous 信道。它非常適合於傳遞性設計,在這種設計中,在一個線程中運行的對象要將某些信息、事件或任務傳遞給在另一個線程中運行的對象,它就必須與該對象同步。

對於正在等待的生產者和使用者線程而言,此類支持可選的公平排序策略。默認情況下不保證這種排序。但是,使用公平設置爲 true 所構造的隊列可保證線程以 FIFO 的順序進行訪問。公平通常會降低吞吐量,但是可以減小可變性並避免得不到服務。

此類及其迭代器實現 Collection 和 Iterator 接口的所有可選 方法。

此類是 Java Collections Framework 的成員。

二、核心方法

1、put

public void put(E o) throws InterruptedException {
        if (o == null) throw new NullPointerException();
        if (transferer.transfer(o, false, 0) == null) {
            Thread.interrupted();
            throw new InterruptedException();
        }
    }

2、offer

 public boolean offer(E e) {
        if (e == null) throw new NullPointerException();
        return transferer.transfer(e, true, 0) != null;
    }

3、take

public E take() throws InterruptedException {
        Object e = transferer.transfer(null, false, 0);
        if (e != null)
            return (E)e;
        Thread.interrupted();
        throw new InterruptedException();
    }

4、poll

 public E poll() {
        return (E)transferer.transfer(null, true, 0);
    }

由此我們可以發現,這四個隊列的操作方法都依靠transfer來實現,在 SynchronousQueue 定義了一個抽象類 Transfer,並且提供了兩種不同的數據結構實現了該抽象類TransferStack和TransferQueue,如果SynchronousQueue  使用公平策略,則用TransferQueue,如果是非公平策略,則用使用TransferStack

三、內部實現類TransferStack

1、數據結構SNode

該結構是一個鏈表結構,並且裏面的屬性next,match以及waiter都是volatile類型的
該結構提供了tryMatch的方法,將match指向給定的SNode節點,並且如果給定的節點有等待的線程,則置爲null,並且解除等待線程的阻塞狀態
該結構提供的tryCancel方法,則是使用cas算法將match指向自身。
 /** Node class for TransferStacks. */
        static final class SNode {
            volatile SNode next;        // next node in stack
            volatile SNode match;       // the node matched to this
            volatile Thread waiter;     // to control park/unpark
            Object item;                // data; or null for REQUESTs
            int mode;
            // Note: item and mode fields don't need to be volatile
            // since they are always written before, and read after,
            // other volatile/atomic operations.

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

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

            /**
             * Tries to match node s to this node, if so, waking up thread.
             * Fulfillers call tryMatch to identify their waiters.
             * Waiters block until they have been matched.
             *
             * @param s the node to match
             * @return true if successfully matched to s
             */
            //如果match對象爲空,則使用cas算法將match指向給定的節點s,
            //如果節點s有等待的線程,則置爲空,並且解除等待線程的阻塞方法
            boolean tryMatch(SNode s) {
                if (match == null &&											//如果match爲空,則將當前節點的match 置爲節點s
                    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;
            }

            /**
             * Tries to cancel a wait by matching node to itself.
             */
            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);
                }
            }
        }


 2、核心方法 transfer

Object transfer(Object e, boolean timed, long nanos) {
            /*
             * Basic algorithm is to loop trying one of three actions:
             *
             * 1. If apparently empty or already containing nodes of same
             *    mode, try to push node on stack and wait for a match,
             *    returning it, or null if cancelled.
             *
             * 2. If apparently containing node of complementary mode,
             *    try to push a fulfilling node on to stack, match
             *    with corresponding waiting node, pop both from
             *    stack, and return matched item. The matching or
             *    unlinking might not actually be necessary because of
             *    other threads performing action 3:
             *
             * 3. If top of stack already holds another fulfilling node,
             *    help it out by doing its match and/or pop
             *    operations, and then continue. The code for helping
             *    is essentially the same as for fulfilling, except
             *    that it doesn't return the item.
             */

            SNode s = null; // constructed/reused as needed
            int mode = (e == null) ? REQUEST : DATA;//如果e爲空則說明是出棧(take,poll)操作,否則是入棧(put,offer)操作,e代表入棧的數據

            for (;;) {
                SNode h = head;
		// 如果棧頂爲空或者棧頂的模式和 傳遞過來的請求模式一樣,
		//ps:一開始的話 棧頂肯定爲空;請求模式一樣,可以理解爲當前操作與棧頂是同類型的操作,都是入棧或者出棧
                if (h == null || h.mode == mode) {  // empty or same-mode 
                    if (timed && nanos <= 0) {      // can't wait         //如果有超時限制,這塊暫時不研究
                        if (h != null && h.isCancelled())                 
                            casHead(h, h.next);     // pop cancelled node 
                        else
                            return null;                                  
                    } else if (casHead(h, s = snode(s, e, h, mode))) {    //首先創建新的節點s並且壓棧,
			//等待線程中斷或者阻塞或者節點s的match元素不爲空
			//如果線程中斷,則節點s的match指向自身,
			//循環完畢N次之後,設置s節點的等待線程爲當前線程,如果沒有超時限制,則調用方法
			//LockSupport.park(this); 來阻塞當前線程,
			//直到 其它操作獲取到節點s之後,操作s的match元素
			//之後獲取到s節點的等待線程,通過 unpark 解除線程的阻塞狀態
                        SNode m = awaitFulfill(s, timed, nanos);          //
                        if (m == s) {               // wait was cancelled//如果線程被中斷,則返回null
                            clean(s);
                            return null;
                        }
			//如果棧頂不爲空,並且棧頂指向的next是s,則將棧頂置換爲s指向的下個節點
                        if ((h = head) != null && h.next == s)
                            casHead(h, s.next);     // help s's fulfiller
			//如果當前mode是出棧操作,則返回m的item,如果當前mode是入棧操作,則返回s的item
			//注意此時的棧的數據結構是 s是新的節點,m 是s的match元素,
			//如果是入棧,則說明前一操作是出棧,但是沒有數據,所以一直在等待,這時候入棧,正好滿足需求
			//如過是出棧,則說明前一操作是入棧,所以可以直接獲取到入棧的數據		
                        return (mode == REQUEST) ? m.item : s.item;
                    }
                } else if (!isFulfilling(h.mode)) { // try to fulfill	  此時可以確定head!=null&&h.mode!=mode                    
		    //如果發生了取消,則h的match指向自身,置換棧頂(h)的元素爲h的next節點
		    if (h.isCancelled())            // already cancelled  
                        casHead(h, h.next);         // pop and retry      
                    else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) {//創建新的節點s,並且壓棧
                        for (;;) { // loop until matched or waiters disappear
                            SNode m = s.next;       // m is s's match        //獲取s的next節點,相當於舊的head
                            if (m == null) {        // all waiters are gone  //重複檢查如果m 爲空, 
                                casHead(s, null);   // pop fulfill node      //如果此時的棧頂元素爲s,那麼將棧頂元素置爲空
                                s = null;           // use new node next time 
                                break;              // restart main loop
                            }
                            SNode mn = m.next;				     //獲取m的下一個節點 mn
                            if (m.tryMatch(s)) {                             //如果節點m的match設置爲s成功,
                                casHead(s, mn);     // pop both s and m      //如果棧頂元素爲s,將棧頂元素置爲mn,
                                return (mode == REQUEST) ? m.item : s.item;  
                            } else                  // lost match
                                s.casNext(m, mn);   // help unlink
                        }
                    }
                } else {                            // help a fulfiller
                    SNode m = h.next;               // m is h's match
                    if (m == null)                  // waiter is gone
                        casHead(h, null);           // pop fulfilling node
                    else {
                        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
                    }
                }
            }
        }


3、核心方法 awaitFulfill

 	    long lastTime = timed ? System.nanoTime() : 0;               //是否設置了超時設置
            Thread w = Thread.currentThread();
            SNode h = head;
            int spins = (shouldSpin(s) ?                                 
                         (timed ? maxTimedSpins : maxUntimedSpins) : 0);
            for (;;) {
                if (w.isInterrupted())	//如果當前線程被中斷,則調用節點的tryCancel方法,實則是原子操作,如果s的match爲null,則將s的match 指向自身
                    s.tryCancel();
                SNode m = s.match;
                if (m != null)		//如果match 不爲空,則返回m
                    return m;
                if (timed) {
                    long now = System.nanoTime();
                    nanos -= now - lastTime;
                    lastTime = now;
                    if (nanos <= 0) {
                        s.tryCancel();
                        continue;
                    }
                }
                if (spins > 0)		//spins大於0 的時候一直遞減,直到爲0
                    spins = shouldSpin(s) ? (spins-1) : 0;
                else if (s.waiter == null) //此時循環了 spins 次,仍然沒有退出循環,即沒有返回數據,如果節點s的等待線程爲空,則設置爲當前線程
                    s.waiter = w; // establish waiter so can park next iter
                else if (!timed)		//如果沒有設置超時限制,
                    LockSupport.park(this);	//設置當前線程爲阻塞狀態
                else if (nanos > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanos);
            }
        }
個人分析得出一個大致的結論
使用SynchronousQueue,在非公平策略下,隊列的入隊和出隊操作會被封裝成Snode節點並且壓棧,
當前後兩個操作不一致,其中一個是入隊另一個是出隊,這種情況發生的時候,
TransferStack會一次性彈出 棧頂的2個元素,並且設置第一個元素的match 指向第二個元素,並且解除第二個元素等待線程的阻塞狀態。然後將棧頂元素置爲第二個元素的next節點。
如果第一個元素是入隊,那麼第二個元素是出隊,返回入隊的數據;如果第一個元素是出隊操作,第二個元素是入隊,返回入隊的數據,
判斷操作是入隊還是出隊,可以根據傳入transfer方法的  Object e 是否爲空來判斷,如果是入隊,則e不爲空,否則e爲空。
判斷連續的兩個操作是否一致,可以將棧頂的mode 與當前操作的mode 比較是否一致來判斷。
return (mode == REQUEST) ? m.item : s.item;  可以將m理解爲第二個元素,s理解爲第一個元素。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章