同步队列_非公平模式_TransferStack

非公平模式_TransferStack(栈实现LIFO)

工作原理

非公平模式实现的同步队列

    /** Dual stack */
    /**
     * 非公平模式实现的同步队列,内部数据结构是 “栈”
     */
    static final class TransferStack<E> extends Transferer<E> {
                /** 表示Node类型为 请求类型 */
        static final int REQUEST    = 0;
        /** Node represents an unfulfilled producer */
        /** 表示Node类型为 数据类型 */
        static final int DATA       = 1;
        /** Node is fulfilling another unfulfilled DATA or REQUEST */
        /** 表示Node类型为 匹配中类型
         * 假设栈顶元素为 REQUEST-NODE,当前请求类型为 DATA的话,入栈会修改类型为 FULFILLING 【栈顶 & 栈顶之下的一个node】。
         * 假设栈顶元素为 DATA-NODE,当前请求类型为 REQUEST的话,入栈会修改类型为 FULFILLING 【栈顶 & 栈顶之下的一个node】。
         */
        static final int FULFILLING = 2;

        /** Returns true if m has fulfilling bit set. */
        //判断当前模式是否为 匹配中状态。
        static boolean isFulfilling(int m) { return (m & FULFILLING) != 0; }

       //判断当前模式是否为 匹配中状态。
        static boolean isFulfilling(int m) { return (m & FULFILLING) != 0; }

TransferStacks中的节点

       static final class SNode {
            //指向下一个栈帧
            volatile SNode next;        // next node in stack
            //与当前node匹配的节点
            volatile SNode match;       // the node matched to this
            //假设当前node对应的线程 自旋期间未被匹配成功,那么node对应的线程需要挂起,挂起前 waiter 保存对应的线程引用,
            //方便 匹配成功后,被唤醒。
            volatile Thread waiter;     // to control park/unpark
            //数据域,data不为空 表示当前Node对应的请求类型为 DATA类型。 反之则表示Node为 REQUEST类型。
            Object item;                // data; or null for REQUESTs
            //表示当前Node的模式 【DATA/REQUEST/FULFILLING】
            int mode;
           
           
           
            //CAS方式设置Node对象的next字段。
            boolean casNext(SNode cmp, SNode val) {
                //优化:cmp == next  为什么要判断?
                //因为cas指令 在平台执行时,同一时刻只能有一个cas指令被执行。
                //有了java层面的这一次判断,可以提升一部分性能。 cmp == next 不相等,就没必要走 cas指令。
                return cmp == next &&
                    UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
            }
           
           
                        *
             /* 尝试匹配
             * 调用tryMatch的对象是 栈顶节点的下一个节点,与栈顶匹配的节点。
             *
             * @return ture 匹配成功。 否则匹配失败..
             */
            boolean tryMatch(SNode s) {
                //条件一:match == null 成立,说明当前Node尚未与任何节点发生过匹配...
                //条件二 成立:使用CAS方式 设置match字段,表示当前Node已经被匹配了
                if (match == null &&
                    UNSAFE.compareAndSwapObject(this, matchOffset, null, s)) {
                    //当前Node如果自旋结束,那么会使用LockSupport.park 方法挂起,挂起之前会将Node对应的Thread 保留到 waiter字段。
                    Thread w = waiter;
                    //条件成立:说明Node对应的Thread已经挂起了...
                    if (w != null) {    // waiters need at most one unpark
                        waiter = null;
                        //使用unpark方式唤醒。
                        LockSupport.unpark(w);
                    }
                    return true;
                }
                return match == s;
            }
           
           
           
             /**
             * Tries to cancel a wait by matching node to itself.
             * 尝试取消..
             */
            void tryCancel() {
                //match字段 保留当前Node对象本身,表示这个Node是取消状态,取消状态的Node,最终会被 强制 移除出栈。
                UNSAFE.compareAndSwapObject(this, matchOffset, null, this);
            }

            //如果match保留的是当前Node本身,那表示当前Node是取消状态,反之 则 非取消状态。
            boolean isCancelled() {
                return match == this;
            }
           
           
        //表示栈顶指针
        volatile SNode head;


        //设置栈顶元素
        boolean casHead(SNode h, SNode nh) {
            return h == head &&
                UNSAFE.compareAndSwapObject(this, headOffset, h, nh);
        }

         /* @param s  SNode引用,当这个引用指向空时,snode方法会创建一个SNode对象 并且赋值给这个引用
         * @param e SNode对象的item字段
         * @param next 指向当前栈帧的下一个栈帧
         * @param mode REQUEST/DATA/FULFILLING
         */
        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;
        }           

@SuppressWarnings("unchecked")
E transfer(E 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.
     */

    //包装当前线程的Node
    SNode s = null; // constructed/reused as needed
    //e == null 条件成立:当前线程是一个REQUEST线程。
    //否则 e!=null 说明 当前线程是一个DATA线程,提交数据的线程。
    int mode = (e == null) ? REQUEST : DATA;

    //自旋
    for (;;) {
        //h 表示栈顶指针
        SNode h = head;

        //CASE1:当前栈内为空 或者 栈顶Node模式与当前请求模式一致,都是需要做入栈操作。
        if (h == null || h.mode == mode) {  // empty or same-mode

            //条件一:成立,说明当前请求是指定了 超时限制的
            //条件二:nanos <= 0 , nanos == 0. 表示这个请求 不支持 “阻塞等待”。 queue.offer();
            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))  入栈操作。
            else if (casHead(h, s = snode(s, e, h, mode))) {
                //执行到这里,说明 当前请求入栈成功。
                //入栈成功之后要做什么呢?
                //在栈内等待一个好消息,等待被匹配!

                //awaitFulfill 等待被匹配的逻辑...
                //1.正常情况:返回匹配的节点
                //2.取消情况:返回当前节点  s节点进去,返回s节点...
                SNode m = awaitFulfill(s, timed, nanos);


                //条件成立:说明当前Node状态是 取消状态...
                if (m == s) {               // wait was cancelled
                    //将取消状态的节点 出栈...
                    clean(s);
                    //取消状态 最终返回null
                    return null;
                }
                //执行到这里 说明当前Node已经被匹配了...

                //条件一:成立,说明栈顶是有Node
                //条件二:成立,说明 Fulfill 和 当前Node 还未出栈,需要协助出栈。
                if ((h = head) != null && h.next == s)
                    //将fulfill 和 当前Node 结对 出栈
                    casHead(h, s.next);     // help s's fulfiller

                //当前NODE模式为REQUEST类型:返回匹配节点的m.item 数据域
                //当前NODE模式为DATA类型:返回Node.item 数据域,当前请求提交的 数据e
                return (E) ((mode == REQUEST) ? m.item : s.item);
            }
        }
        //什么时候来到这??
        //栈顶Node的模式与当前请求的模式不一致,会执行else if 的条件。
        //栈顶是 (DATA  Reqeust)    (Request   DATA)   (FULFILLING  REQUEST/DATA)
        //CASE2:当前栈顶模式与请求模式不一致,且栈顶不是FULFILLING
        else if (!isFulfilling(h.mode)) { // try to fulfill
            //条件成立:说明当前栈顶状态为 取消状态,当前线程协助它出栈。
            if (h.isCancelled())            // already cancelled
                //协助 取消状态节点 出栈。
                casHead(h, h.next);         // pop and retry



            //条件成立:说明压栈节点成功,入栈一个 FULFILLING | mode  NODE
            else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) {
                //当前请求入栈成功

                //自旋,fulfill 节点 和 fulfill.next 节点进行匹配工作...
                for (;;) { // loop until matched or waiters disappear
                    //m 与当前s 匹配节点。
                    SNode m = s.next;       // m is s's match

                    //m == null 什么时候可能成立呢?
                    //当s.next节点 超时或者被外部线程中断唤醒后,会执行 clean 操作 将 自己清理出栈,此时
                    //站在匹配者线程 来看,真有可能拿到一个null。
                    if (m == null) {        // all waiters are gone
                        //将整个栈清空。
                        casHead(s, null);   // pop fulfill node
                        s = null;           // use new node next time
                        //回到外层大的 自旋中,再重新选择路径执行,此时有可能 插入一个节点。
                        break;              // restart main loop
                    }

                    //什么时候会执行到这里呢?
                    //fulfilling 匹配节点不为null,进行真正的匹配工作。

                    //获取 匹配节点的 下一个节点。
                    SNode mn = m.next;
                    //尝试匹配,匹配成功,则将fulfilling 和 m 一起出栈。
                    if (m.tryMatch(s)) {
                        //结对出栈
                        casHead(s, mn);     // pop both s and m

                        //当前NODE模式为REQUEST类型:返回匹配节点的m.item 数据域
                        //当前NODE模式为DATA类型:返回Node.item 数据域,当前请求提交的 数据e
                        return (E) ((mode == REQUEST) ? m.item : s.item);
                    } else                  // lost match
                        //强制出栈
                        s.casNext(m, mn);   // help unlink
                }

            }
        }

        //CASE3:什么时候会执行?
        //栈顶模式为 FULFILLING模式,表示栈顶和栈顶下面的栈帧正在发生匹配...
        //当前请求需要做 协助 工作。
        else {                            // help a fulfiller
            //h 表示的是 fulfilling节点,m fulfilling匹配的节点。
            SNode m = h.next;               // m is h's match
            //m == null 什么时候可能成立呢?
            //当s.next节点 超时或者被外部线程中断唤醒后,会执行 clean 操作 将 自己清理出栈,此时
            //站在匹配者线程 来看,真有可能拿到一个null。
            if (m == null)                  // waiter is gone
                //清空栈
                casHead(h, null);           // pop fulfilling node

            //大部分情况:走else分支。
            else {
                //获取栈顶匹配节点的 下一个节点
                SNode mn = m.next;
                //条件成立:说明 m 和 栈顶 匹配成功
                if (m.tryMatch(h))          // help match
                    //双双出栈,让栈顶指针指向 匹配节点的下一个节点。
                    casHead(h, mn);         // pop both h and m
                else                        // lost match
                    //强制出栈
                    h.casNext(m, mn);       // help unlink
            }
        }
    }
}

transfer -> awaitFulfill

 /*
 * @param s 当前请求Node
 * @param timed 当前请求是否支持 超时限制
 * @param nanos 如果请求支持超时限制,nanos 表示超时等待时长。
 */
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.
     */


    //等待的截止时间。  timed == true  =>  System.nanoTime() + nanos
    final long deadline = timed ? System.nanoTime() + nanos : 0L;

    //获取当前请求线程..
    Thread w = Thread.currentThread();

    //spins 表示当前请求线程 在 下面的 for(;;) 自旋检查中,自旋次数。 如果达到spins自旋次数时,当前线程对应的Node 仍然未被匹配成功,
    //那么再选择 挂起 当前请求线程。
    int spins = (shouldSpin(s) ?
                  //timed == true 指定了超时限制的,这个时候采用 maxTimedSpins == 32 ,否则采用 32 * 16
                 (timed ? maxTimedSpins : maxUntimedSpins) : 0);

    //自旋检查逻辑:1.是否匹配  2.是否超时  3.是否被中断..
    for (;;) {


        //条件成立:说明当前线程收到中断信号,需要设置Node状态为 取消状态。
        if (w.isInterrupted())
            //Node对象的 match 指向 当前Node 说明该Node状态就是 取消状态。
            s.tryCancel();


        //m 表示与当前Node匹配的节点。
        //1.正常情况:有一个请求 与 当前Node 匹配成功,这个时候 s.match 指向 匹配节点。
        //2.取消情况:当前match 指向 当前Node...
        SNode m = s.match;

        if (m != null)
            //可能正常 也可能是 取消...
            return m;



        //条件成立:说明指定了超时限制..
        if (timed) {
            //nanos 表示距离超时 还有多少纳秒..
            nanos = deadline - System.nanoTime();
            //条件成立:说明已经超时了...
            if (nanos <= 0L) {
                //设置当前Node状态为 取消状态.. match-->当前Node
                s.tryCancel();
                continue;
            }
        }





        //条件成立:说明当前线程还可以进行自旋检查...
        if (spins > 0)
            //自旋次数 累积 递减。。。
            spins = shouldSpin(s) ? (spins-1) : 0;


        //spins == 0 ,已经不允许再进行自旋检查了
        else if (s.waiter == null)
            //把当前Node对应的Thread 保存到 Node.waiter字段中..
            s.waiter = w; // establish waiter so can park next iter

        //条件成立:说明当前Node对应的请求  未指定超时限制。
        else if (!timed)
            //使用不指定超时限制的park方法 挂起当前线程,直到 当前线程被外部线程 使用unpark唤醒。
            LockSupport.park(this);

        //什么时候执行到这里? timed == true 设置了 超时限制..
        //条件成立:nanos > 1000 纳秒的值,只有这种情况下,才允许挂起当前线程..否则 说明 超时给的太少了...挂起和唤醒的成本 远大于 空转自旋...
        else if (nanos > spinForTimeoutThreshold)
            LockSupport.parkNanos(this, nanos);
    }
}

awaitFulfill -》 shouldSpin

boolean shouldSpin(SNode s) {
    //获取栈顶
    SNode h = head;
    //条件一 h == s :条件成立 说明当前s 就是栈顶,允许自旋检查...
    //条件二 h == null : 什么时候成立? 当前s节点 自旋检查期间,又来了一个 与当前s 节点匹配的请求,双双出栈了...条件会成立。
    //条件三 isFulfilling(h.mode) : 前提 当前 s 不是 栈顶元素。并且当前栈顶正在匹配中,这种状态 栈顶下面的元素,都允许自旋检查。
    return (h == s || h == null || isFulfilling(h.mode));
}

transfer -》 clean

void clean(SNode s) {
    //清空数据域
    s.item = null;   // forget item
    //释放线程引用..
    s.waiter = null; // forget thread
    /*
     * At worst we may need to traverse entire stack to unlink
     * s. If there are multiple concurrent calls to clean, we
     * might not see s if another thread has already removed
     * it. But we can stop when we see any node known to
     * follow s. We use s.next unless it too is cancelled, in
     * which case we try the node one past. We don't check any
     * further because we don't want to doubly traverse just to
     * find sentinel.
     */
    //检查取消节点的截止位置
    SNode past = s.next;

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

    // Absorb cancelled nodes at head
    //当前循环检查节点
    SNode p;
    //从栈顶开始向下检查,将栈顶开始向下连续的 取消状态的节点 全部清理出去,直到碰到past为止。
    while ((p = head) != null && p != past && p.isCancelled())
        casHead(p, p.next);

    // Unsplice embedded nodes
    while (p != null && p != past) {
        //获取p.next
        SNode n = p.next;
        if (n != null && n.isCancelled())
            // n-> x -> y   ===>  n -> y
            p.casNext(n, n.next);
        else
            p = n;
    }
}

难点分析

//CASE2:当前栈顶模式与请求模式不一致,且栈顶不是FULFILLING
else if (!isFulfilling(h.mode)) { // try to fulfill
->
else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) {
 

->
        //m == null 什么时候可能成立呢?  / m为什么等于null
        if (m == null) {        // all waiters are gone
            //将整个栈清空。
            casHead(s, null);   // pop fulfill node
            s = null;           // use new node next time
            //回到外层大的 自旋中,再重新选择路径执行,此时有可能 插入一个节点。
            break;              // restart main loop
        }

案例分析
public class TransferStackDemo01 {
    public static void main(String[] args) throws InterruptedException {
        SynchronousQueue<Integer> queue = new SynchronousQueue<>();

        Thread thread1 = new Thread(() -> {
            try {
                queue.put(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread1.start();

        Thread.sleep(2000);

        Thread thread2 = new Thread(() -> {
            try {
                queue.put(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread2.start();

        Thread.sleep(2000);

        Thread thread3 = new Thread(() -> {
            try {
                queue.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread3.start();

    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值