• SynchronousQueue 源码分析


    SynchronousQueue

    SynchronousQueue 能解决什么问题?什么时候使用 SynchronousQueue?

    1)SynchronousQueue 没有任何内部容量。
    2)SynchronousQueue 中每一个插入操作必须等待一个与之配对的删除操作,反之亦然。
    3)SynchronousQueue 非常适合于线程之间交换信息,均衡生产者与消费者的处理速率。
    4)SynchronousQueue 的性能和吞吐量高于 LinkedBlockingQueue 和 ArrayBlockingQueue。
    

    如何使用 SynchronousQueue?

    1)Executors.newCachedThreadPool() 使用 SynchronousQueue 作为其任务队列。
    2)需要均衡生产和消费速率时,可以使用 SynchronousQueue。
    

    使用 SynchronousQueue 有什么风险?

    1)生产速率持续高于消费速率时,会导致生产者阻塞,服务不可用。
    

    SynchronousQueue 核心操作的实现原理?

    • 创建实例
        /**
         * Shared internal API for dual stacks and queues.
         */
        abstract static class Transferer<E> {
            /**
             *  执行一个 put 或 take 操作
             *
             * @param e 1)值为 null,则表示这是一个 take 操作
             *          2)值不为 null,则表示这是一个 put 操作
             * @param timed 操作的超时时间
             * @param nanos 以纳秒为单位
             * @return  1)值为 null 表示超时或线程被中断
             *          2)值不为 null,表示插入或读取的目标元素
             */
            abstract E transfer(E e, boolean timed, long nanos);
        }
    
        /**
         *  超时等待前的自旋次数
         */
        static final int MAX_TIMED_SPINS =
                Runtime.getRuntime().availableProcessors() < 2 ? 0 : 32;
    
        /**
         *  阻塞等待前的自旋次数
         */
        static final int MAX_UNTIMED_SPINS = SynchronousQueue.MAX_TIMED_SPINS * 16;
    
        /**
         *  超时时间小于 1000 纳秒时,自旋比阻塞等待效率高
         */
        static final long SPIN_FOR_TIMEOUT_THRESHOLD = 1000L;
    
        /**
         *  转移接口
         */
        private transient volatile Transferer<E> transferer;
    
        /**
         *  创建一个非公平的同步队列
         */
        public SynchronousQueue() {
            this(false);
        }
    
        /**
         *  fair==true,创建一个公平的同步队列,最先等待的最先释放
         *  fair==false,创建一个非公平的同步队列
         */
        public SynchronousQueue(boolean fair) {
            transferer = fair ? new TransferQueue<>() : new TransferStack<>();
        }
    
    • 将目标元素添加到同步队列,如果当前没有消费者,则阻塞等待
        /**
         *  将目标元素添加到同步队列,如果当前没有消费者,则阻塞等待
         */
        @Override
        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();
            }
        }
    
        /** Dual stack */
        static final class TransferStack<E> extends Transferer<E> {
            /** 节点代表一个未完成的消费者 */
            static final int REQUEST    = 0;
            /** 节点代表一个未完成的生产者 */
            static final int DATA       = 1;
            /** 节点正在完成一个匹配的操作 */
            static final int FULFILLING = 2;
    
            /** Node class for TransferStacks. */
            static final class SNode {
                // 堆栈的下一个节点
                volatile SNode next;
                // 与当前节点匹配的对偶节点
                volatile SNode match;
                // 当前节点驻留的线程
                volatile Thread waiter;
                // 如果是数据节点则非空,如果是请求节点则为 null
                Object item;
                // 当前节点的状态
                int mode;
    
                SNode(Object item) {
                    this.item = item;
                }
    
                /**
                 *  参数原子更新 next 节点
                 */
                boolean casNext(SNode cmp, SNode val) {
                    return cmp == next &&
                            SNode.SNEXT.compareAndSet(this, cmp, val);
                }
    
                /**
                 *  尝试将目标节点匹配给当前节点,如果成功则唤醒驻留在当前节点的线程
                 */
                boolean tryMatch(SNode s) {
                    // 尝试设置匹配节点为 s
                    if (match == null &&
                            SNode.SMATCH.compareAndSet(this, null, s)) {
                        // 读取驻留线程
                        final Thread w = waiter;
                        if (w != null) {    // waiters need at most one unpark
                            waiter = null;
                            // 唤醒驻留线程
                            LockSupport.unpark(w);
                        }
                        // 匹配成功返回 true
                        return true;
                    }
                    return match == s;
                }
    
                /**
                 *  将匹配节点设置为 this 表示节点被删除
                 */
                void tryCancel() {
                    SNode.SMATCH.compareAndSet(this, null, this);
                }
    
                /**
                 *  当前节点已经被取消
                 * created by ZXD at 8 Dec 2018 T 11:11:25
                 */
                boolean isCancelled() {
                    return match == this;
                }
    
                // VarHandle mechanics
                private static final VarHandle SMATCH;
                private static final VarHandle SNEXT;
                static {
                    try {
                        final MethodHandles.Lookup l = MethodHandles.lookup();
                        SMATCH = l.findVarHandle(SNode.class, "match", SNode.class);
                        SNEXT = l.findVarHandle(SNode.class, "next", SNode.class);
                    } catch (final ReflectiveOperationException e) {
                        throw new Error(e);
                    }
                }
            }  
        
            /** 堆栈顶部元素 */
            volatile SNode head;
    
            /**
             * Puts or takes an item.
             */
            @Override
            @SuppressWarnings("unchecked")
            E transfer(E e, boolean timed, long nanos) {
                /**
                 *  1)如果堆栈为空或包含相同类型的节点,则新建节点并入栈,阻塞等待。
                 *  2)如果堆栈中包含对偶节点,则将一个对偶节点入栈,
                 *      并同时弹出新增节点及与其匹配的节点。
                 *  3)如果堆栈顶部已经持有对偶节点,则帮助它们弹出堆栈。
                 */
                SNode s = null; // constructed/reused as needed
                // 计算节点模式
                final int mode = e == null ? TransferStack.REQUEST : TransferStack.DATA;
                for (;;) {
                    // 读取栈顶节点
                    SNode h = head;
                    // 1)堆栈为空或栈顶元素和新增节点模式一致
                    if (h == null || h.mode == mode) {  // empty or same-mode
                        // 1-1)如果设置了超时时间,并且已经超时
                        if (timed && nanos <= 0L) {     // can't wait
                            // 头节点不为 null && 头节点已经被取消
                            if (h != null && h.isCancelled()) {
                                // 尝试原子更新栈顶节点
                                casHead(h, h.next);     // pop cancelled node
                            } else {
                                // 删除栈顶后返回 null
                                return null;
                            }
                            // 1-2)尝试将新节点入栈
                        } else if (casHead(h, s = TransferStack.snode(s, e, h, mode))) {
                            // 尝试在指定的超时时间内等待对偶节点
                            final SNode m = awaitFulfill(s, timed, nanos);
                            // 线程超时或被其他线程中断
                            if (m == s) {               // wait was cancelled
                                // 清除新增节点
                                clean(s);
                                return null;
                            }
                            // 当前节点和对偶节点位于栈顶前两个位置,则弹出它们
                            if ((h = head) != null && h.next == s)
                            {
                                casHead(h, s.next);     // help s's fulfiller
                            }
                            /**
                             *  1)新值的是请求节点,则返回对偶节点的数据值
                             *  2)新值的是数据节点,则返回其持有的数据
                             */
                            return (E) (mode == TransferStack.REQUEST ? m.item : s.item);
                        }
                    // 2)头节点不是 fulFill 节点   
                    } else if (!TransferStack.isFulfilling(h.mode)) { // try to fulfill
                        // 1)头节点已经被取消
                        if (h.isCancelled()) {
                            // 则重新写入头结点
                            casHead(h, h.next);         // pop and retry
                        // 2)尝试写入一个 fulFill 节点到栈顶   
                        } else if (casHead(h, s=TransferStack.snode(s, e, h, TransferStack.FULFILLING|mode))) {
                            for (;;) { // loop until matched or waiters disappear
                                // 读取匹配节点
                                final SNode m = s.next;       // m is s's match
                                // 已经不存在等待节点【被其他线程帮助弹出了】
                                if (m == null) {        // all waiters are gone
                                    // 写入头节点
                                    casHead(s, null);   // pop fulfill node
                                    s = null;           // use new node next time
                                    break;              // restart main loop
                                }
                                // 读取匹配节点的后置节点
                                final SNode mn = m.next;
                                // 如果仍处于匹配模式
                                if (m.tryMatch(s)) {
                                    // 同时弹出这两个节点
                                    casHead(s, mn);     // pop both s and m
                                    /**
                                     *  1)新值的是请求节点,则返回对偶节点的数据值
                                     *  2)新值的是数据节点,则返回其持有的数据
                                     */ 
                                    return (E) (mode == TransferStack.REQUEST ? m.item : s.item);
                                }
                                else {
                                    s.casNext(m, mn);   // help unlink
                                }
                            }
                        }
                    // 3)头结点是一个 fulFill 节点,则帮助将它和它的对偶节点出栈   
                    } else {                            // help a fulfiller
                        final SNode m = h.next;               // m is h's match
                        if (m == null) {
                            casHead(h, null);           // pop fulfilling node
                        } else {
                            final SNode mn = m.next;
                            if (m.tryMatch(h)) {
                                casHead(h, mn);         // pop both h and m
                            }
                            else {
                                h.casNext(m, mn);       // help unlink
                            }
                        }
                    }
                }
            }
    
            /**
             *  尝试原子更新头结点
             * created by ZXD at 8 Dec 2018 T 10:56:47
             * @param h
             * @param nh
             * @return
             */
            boolean casHead(SNode h, SNode nh) {
                return h == head &&
                        TransferStack.SHEAD.compareAndSet(this, h, nh);
            }
    
            /**
             *  将节点加入栈顶
             */
            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;
            }
    
            /**
             *  线程执行自旋或阻塞等待,直到堆栈中写入一个对偶节点、线程超时、被其他线程中断
             *
             * @param s 等待节点
             * @param timed 是否是超时模式
             * @param nanos 超时纳秒
             * @return  1)出现对偶节点,则返回对偶节点
             *          2)线程超时或被中断,则返回 s
             */
            SNode awaitFulfill(SNode s, boolean timed, long nanos) {
                /*
                 * When a node/thread is about to block, it sets its waiter
                 * field and then rechecks state at least one more time
                 * before actually parking, thus covering race vs
                 * fulfiller noticing that waiter is non-null so should be
                 * woken.
                 *
                 * When invoked by nodes that appear at the point of call
                 * to be at the head of the stack, calls to park are
                 * preceded by spins to avoid blocking when producers and
                 * consumers are arriving very close in time.  This can
                 * happen enough to bother only on multiprocessors.
                 *
                 * The order of checks for returning out of main loop
                 * reflects fact that interrupts have precedence over
                 * normal returns, which have precedence over
                 * timeouts. (So, on timeout, one last check for match is
                 * done before giving up.) Except that calls from untimed
                 * SynchronousQueue.{poll/offer} don't check interrupts
                 * and don't wait at all, so are trapped in transfer
                 * method rather than calling awaitFulfill.
                 */
                // 计算截止时间
                final long deadline = timed ? System.nanoTime() + nanos : 0L;
                // 读取当前线程
                final Thread w = Thread.currentThread();
                // 计算自旋次数
                int spins = shouldSpin(s)
                        ? timed ? SynchronousQueue.MAX_TIMED_SPINS : SynchronousQueue.MAX_UNTIMED_SPINS
                                : 0;
                for (;;) {
                    // 线程已经被中断
                    if (w.isInterrupted()) {
                        // 则尝试删除节点
                        s.tryCancel();
                    }
                    // 读取匹配节点
                    final SNode m = s.match;
                    /**
                     *  如果存在匹配节点,则返回它
                     *  1)节点本身
                     *  2)对偶节点
                     */
                    if (m != null) {
                        return m;
                    }
                    // 如果是超时模式
                    if (timed) {
                        // 计算剩余时间
                        nanos = deadline - System.nanoTime();
                        if (nanos <= 0L) {
                            // 已经超时,则尝试删除节点
                            s.tryCancel();
                            continue;
                        }
                    }
                    // 1)尝试进行自旋
                    if (spins > 0) {
                        // 线程执行自旋
                        Thread.onSpinWait();
                        // 重新计算值
                        spins = shouldSpin(s) ? spins - 1 : 0;
                    }
                    // 2)节点的驻留线程为 null
                    else if (s.waiter == null) {
                        // 写入自旋线程
                        s.waiter = w; // establish waiter so can park next iter
                    // 3)如果不是超时阻塞   
                    } else if (!timed) {
                        // 阻塞当前线程
                        LockSupport.park(this);
                    // 4)超时时间 > 1000 纳秒 
                    } else if (nanos > SynchronousQueue.SPIN_FOR_TIMEOUT_THRESHOLD) {
                        // 超时阻塞当前线程
                        LockSupport.parkNanos(this, nanos);
                    }
                }
            }
    
            /**
             * Unlinks s from the stack.
             */
            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;
                }
    
                /**
                 *  从头部开始遍历,删除已经取消的节点,
                 *  1)直到发现一个未取消的节点 ||
                 *  2)一直遍历到 past 为止
                 */
                SNode p;
                while ((p = head) != null && p != past && p.isCancelled()) {
                    casHead(p, p.next);
                }
                
                // 发现了一个未取消的节点,从未取消的节点开始又执行一次清除操作
                while (p != null && p != past) {
                    // 读取后置节点
                    final SNode n = p.next;
                    // 后置节点已经取消
                    if (n != null && n.isCancelled()) {
                        // 则将其踢除
                        p.casNext(n, n.next);
                    } else {
                        // 处理下一个节点
                        p = n;
                    }
                }
            }
        }
    
    • 如果队列头部是一个 take 操作,则将当前元素传递给它,并返回 true,否则立刻返回 false
        /**
         *  如果栈顶是一个 take 操作,则将当前元素传递给它,并返回 true,否则立刻返回 false
         */
        @Override
        public boolean offer(E e) {
            if (e == null) {
                throw new NullPointerException();
            }
            return transferer.transfer(e, true, 0) != null;
        }
    
    • 尝试在指定的超时时间内将目标元素 e 传递给队列头部的一个 take 操作,传递成功则返回 true,否则返回 false。
        /**
         *  尝试在指定的超时时间内将目标元素 e 传递给一个 take 操作,
         *  传递成功则返回 true,否则返回 false
         */
        @Override
        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;
            }
            // 线程未被中断,则返回 false;否则抛出 InterruptedException 异常
            if (!Thread.interrupted()) {
                return false;
            }
            throw new InterruptedException();
        }
    
    • 移除并获取队列头元素,如果无可用元素,则阻塞等待
        /**
         *  移除并获取栈顶元素,如果无可用元素,则阻塞等待
         */
        @Override
        public E take() throws InterruptedException {
            final E e = transferer.transfer(null, false, 0);
            if (e != null) {
                return e;
            }
            // 返回元素为 null 表示线程被中断,则清除中断标识并抛出  InterruptedException 异常。
            Thread.interrupted();
            throw new InterruptedException();
        }
    
    • 如果队列头节点为一个数据节点,则尝试移除并返回队列头部的数据元素,否则返回 null
        /**
         *  尝试移除并返回栈顶的数据元素,如果栈顶节点为一个数据节点,否则返回 null
         */
        @Override
        public E poll() {
            return transferer.transfer(null, true, 0);
        }
    
    • 尝试移除并返回队列头部数据元素,如果不存在,则在指定的超时时间内阻塞等待其他线程插入数据,超时则返回 null。
        /**
         *  尝试移除并返回队列头部数据元素,如果不存在,则在指定的超时时间内阻塞等待其他线程插入数据。
         *  超时则返回 null。
         */
        @Override
        public E poll(long timeout, TimeUnit unit) throws InterruptedException {
            final E e = transferer.transfer(null, true, unit.toNanos(timeout));
            if (e != null || !Thread.interrupted()) {
                return e;
            }
            throw new InterruptedException();
        }
    
  • 相关阅读:
    WCF寻址
    WCF之多个协定
    WCF之多个终结点
    WCF客户端和服务端配置
    VS2012新建项目出错:未找到与约束ContractName Microsoft.VisualStudio.Text.ITextDocumentFactoryService
    windows运行打开服务命令
    Android中 服务里的方法抽取成接口
    Android Studio 使用genymotion 模拟器运行app时 提示找不到任何设备
    SVN 首次用TortoiseSVN Checkout 提示Unexpected HTTP status 405
    jQuery EasyUI -onblu、onkeyup等事件
  • 原文地址:https://www.cnblogs.com/zhuxudong/p/10087189.html
Copyright © 2020-2023  润新知