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理解为第一个元素。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值