同步队列_公平模式_TransferQueue

这篇博客详细解析了公平模式下同步队列`TransferQueue`的实现,包括节点结构`QNode`、队列操作如入队、出队、取消等逻辑,并通过案例分析了不同场景下的行为。主要涉及线程安全、并发控制和队列操作的优化。

公平模式_TransferQueue(队列实现)

公平模式实现的同步队列

/** Dual Queue */
/**
 * 公平模式同步队列
 */
static final class TransferQueue<E> extends Transferer<E> {

    static final class QNode {
        //指向当前节点的下一个节点,组装链表使用的。
        volatile QNode next;          // next node in queue
        //数据域  Node代表的是DATA类型,item表示数据   否则 Node代表的REQUEST类型,item == null
        volatile Object item;         // CAS'ed to or from null
        //当Node对应的线程 未匹配到节点时,对应的线程 最终会挂起,挂起之前会保留 线程引用到waiter ,
        //方法 其它Node匹配当前节点时 唤醒 当前线程..
        volatile Thread waiter;       // to control park/unpark
        //true 当前Node是一个DATA类型   false表示当前Node是一个REQUEST类型。
        final boolean isData;


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

        //修改当前节点next引用
        boolean casNext(QNode cmp, QNode val) {
            return next == cmp &&
                UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
        }

        //修改当前节点数据域item
        boolean casItem(Object cmp, Object val) {
            return item == cmp &&
                UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
        }

        /**
         * Tries to cancel by CAS'ing ref to this as item.
         * 尝试取消当前node
         * 取消状态的Node,它的item域,指向自己Node。
         */
        void tryCancel(Object cmp) {
            UNSAFE.compareAndSwapObject(this, itemOffset, cmp, this);
        }

        //判断当前Node是否为取消状态
        boolean isCancelled() {
            return item == this;
        }

        /**
         * Returns true if this node is known to be off the queue
         * because its next pointer has been forgotten due to
         * an advanceHead operation.
         *
         * 判断当前节点是否 “不在” 队列内,当next指向自己时,说明节点已经出队。
         */
        boolean isOffList() {
            return next == this;
        }
        
        
        //指向队列的dummy节点
        transient volatile QNode head;
        /** Tail of queue */
        //指向队列的尾节点。
        transient volatile QNode tail;
        
        
         /* 表示被清理节点的前驱节点。因为入队操作是 两步完成的,
         * 第一步:t.next = newNode
         * 第二步:tail = newNode
         * 所以,队尾节点出队,是一种非常特殊的情况,需要特殊处理,回头讲!
         */
        transient volatile QNode cleanMe;
        
        
        TransferQueue() {
            QNode h = new QNode(null, false); // initialize to dummy node.
            head = h;
            tail = h;
        }

        /**
         * Tries to cas nh as new head; if successful, unlink
         * old head's next node to avoid garbage retention.
         * 设置头指针指向新的节点,蕴含操作:老的头节点出队。
         */
        void advanceHead(QNode h, QNode nh) {
            if (h == head &&
                UNSAFE.compareAndSwapObject(this, headOffset, h, nh))
                h.next = h; // forget old next
        }

        /**
         * Tries to cas nt as new tail.
         * 更新队尾节点 为新的队尾。
         * @param t 老的队尾
         * @param nt 新的队尾
         */
        void advanceTail(QNode t, QNode nt) {
            if (tail == t)
                UNSAFE.compareAndSwapObject(this, tailOffset, t, nt);
        }
/**
 * Puts or takes an item.
 */
@SuppressWarnings("unchecked")
E transfer(E e, boolean timed, long nanos) {

    //s 指向当前请求 对应Node
    QNode s = null; // constructed/reused as needed
    //isData == true 表示 当前请求是一个写数据操作(DATA)   否则isData == false 表示当前请求是一个 REQUEST操作。
    boolean isData = (e != null);


    //自旋..
    for (;;) {
        QNode t = tail;
        QNode h = head;
        if (t == null || h == null)         // saw uninitialized value
            continue;                       // spin


        //CASE1:入队
        //条件一:成立,说明head和tail同时指向dummy节点,当前队列实际情况 就是 空队列。此时当前请求需要做入队操作,因为没有任何节点 可以去匹配。
        //条件二:队列不是空,队尾节点与当前请求类型是一致的情况。说明也是无法完成匹配操作的情况,此时当前节点只能入队...
        if (h == t || t.isData == isData) { // empty or same-mode
            //获取当前队尾t 的next节点 tn - t.next
            QNode tn = t.next;
            //因为多线程环境,当前线程在入队之前,其它线程有可能已经入队过了..改变了 tail 引用。
            if (t != tail)                    // inconsistent read
                //线程回到自旋...再选择路径执行。
                continue;

            //条件成立:说明已经有线程 入队了,且只完成了 入队的 第一步:设置t.next = newNode, 第二步可能尚未完成..
            if (tn != null) {               // lagging tail
                //协助更新tail 指向新的 尾结点。
                advanceTail(t, tn);
                //线程回到自旋...再选择路径执行。
                continue;
            }

            //条件成立:说明当前调用transfer方法的 上层方法 可能是 offer() 无参的这种方法进来的,这种方法不支持 阻塞等待...
            if (timed && nanos <= 0)        // can't wait
                //检查未匹配到,直接返回null。
                return null;



            //条件成立:说明当前请求尚未 创建对应的node
            if (s == null)
                //创建node过程...
                s = new QNode(e, isData);



            //条件 不成立:!t.casNext(null, s)  说明当前t仍然是tail,当前线程对应的Node入队的第一步 完成!
            if (!t.casNext(null, s))        // failed to link in
                continue;


            //更新队尾 为咱们请求节点。
            advanceTail(t, s);              // swing tail and wait

            //当前节点 等待匹配....
            //当前请求为DATA模式时:e 请求带来的数据
            //x == this 当前SNode对应的线程 取消状态
            //x == null 表示已经有匹配节点了,并且匹配节点拿走了item数据。

            //当前请求为REQUEST模式时:e == null
            //x == this 当前SNode对应的线程 取消状态
            //x != null 且 item != this  表示当前REQUEST类型的Node已经匹配到一个DATA类型的Node了。
            Object x = awaitFulfill(s, e, timed, nanos);


            //说明当前Node状态为 取消状态,需要做 出队逻辑。
            if (x == s) {                   // wait was cancelled
                //清理出队逻辑,最后讲。
                clean(t, s);
                return null;
            }

            //执行到这里说明 当前Node 匹配成功了...
            //1.当前线程在awaitFulfill方法内,已经挂起了...此时运行到这里时是被 匹配节点的线程使用LockSupport.unpark() 唤醒的..
            //被唤醒:当前请求对应的节点,肯定已经出队了,因为匹配者线程 是先让当前Node出队的,再唤醒当前Node对应线程的。

            //2.当前线程在awaitFulfill方法内,处于自旋状态...此时匹配节点 匹配后,它检查发现了,然后返回到上层transfer方法的。
            //自旋状态返回时:当前请求对应的节点,不一定就出队了...


            //被唤醒时:s.isOffList() 条件会成立。  !s.isOffList() 不会成立。
            //条件成立:说明当前Node仍然在队列内,需要做 匹配成功后 出队逻辑。
            if (!s.isOffList()) {           // not already unlinked
                //其实这里面做的事情,就是防止当前Node是自旋检查状态时发现 被匹配了,然后当前线程 需要将
                //当前线程对应的Node做出队逻辑.

                //t 当前s节点的前驱节点,更新dummy节点为 s节点。表示head.next节点已经出队了...
                advanceHead(t, s);          // unlink if head

                //x != null 且 item != this  表示当前REQUEST类型的Node已经匹配到一个DATA类型的Node了。
                //因为s节点已经出队了,所以需要把它的item域 给设置为它自己,表示它是个取消出队状态。
                if (x != null)              // and forget fields
                    s.item = s;
                //因为s已经出队,所以waiter一定要保证是null。
                s.waiter = null;
            }

            //x != null 成立,说明当前请求是REQUEST类型,返回匹配到的数据x
            //x != null 不成立,说明当前请求是DATA类型,返回DATA请求时的e。
            return (x != null) ? (E)x : e;
        }

        //CASE2:队尾节点 与 当前请求节点 互补 (队尾->DATA,请求类型->REQUEST)  (队尾->REQUEST, 请求类型->DATA)
        else {                            // complementary-mode
            //h.next节点 其实是真正的队头,请求节点 与队尾模式不同,需要与队头 发生匹配。因为TransferQueue是一个 公平模式
            QNode m = h.next;               // node to fulfill

            //条件一:t != tail 什么时候成立呢? 肯定是并发导致的,其它线程已经修改过tail了,有其它线程入队过了..当前线程看到的是过期数据,需要重新循环
            //条件二:m == null 什么时候成立呢? 肯定是其它请求先当前请求一步,匹配走了head.next节点。
            //条件三:条件成立,说明已经有其它请求匹配走head.next了。。。当前线程看到的是过期数据。。。重新循环...
            if (t != tail || m == null || h != head)
                continue;                   // inconsistent read


            //执行到这里,说明t m h 不是过期数据,是准确数据。目前来看是准确的!
            //获取匹配节点的数据域 保存到x
            Object x = m.item;


            //条件一:isData == (x != null)
            //isData 表示当前请求是什么类型  isData == true:当前请求是DATA类型  isData == false:当前请求是REQUEST类型。
            //1.假设isData == true   DATA类型
            //m其实表示的是 REQUEST 类型的NODE,它的数据域是 null  => x==null
            //true == (null != null)  => true == false => false

            //2.假设isData == false REQUEST类型
            //m其实表示的是 DATA 类型的NODE,它的数据域是 提交是的e ,并且e != null。
            //false == (obj != null) => false == true => false

            //总结:正常情况下,条件一不会成立。

            //条件二:条件成立,说明m节点已经是 取消状态了...不能完成匹配,当前请求需要continue,再重新选择路径执行了..

            //条件三:!m.casItem(x, e),前提条件 m 非取消状态。
            //1.假设当前请求为REQUEST类型   e == null
            //m 是 DATA类型了...
            //相当于将匹配的DATA Node的数据域清空了,相当于REQUEST 拿走了 它的数据。

            //2.假设当前请求为DATA类型    e != null
            //m 是 REQUEST类型了...
            //相当于将匹配的REQUEST Node的数据域 填充了,填充了 当前DATA 的 数据。相当于传递给REQUEST请求数据了...
            if (isData == (x != null) ||    // m already fulfilled
                x == m ||                   // m cancelled
                !m.casItem(x, e)) {         // lost CAS
                advanceHead(h, m);          // dequeue and retry
                continue;
            }


            //执行到这里,说明匹配已经完成了,匹配完成后,需要做什么?
            //1.将真正的头节点 出队。让这个真正的头结点成为dummy节点
            advanceHead(h, m);              // successfully fulfilled
            //2.唤醒匹配节点的线程..
            LockSupport.unpark(m.waiter);

            //x != null 成立,说明当前请求是REQUEST类型,返回匹配到的数据x
            //x != null 不成立,说明当前请求是DATA类型,返回DATA请求时的e。
            return (x != null) ? (E)x : e;
        }
    }
}

SuppressWarnings -》 awaitFulfill

        Object awaitFulfill(QNode s, E e, boolean timed, long nanos) {
            /* Same idea as TransferStack.awaitFulfill */
            //deadline 表示等待截止时间...
            final long deadline = timed ? System.nanoTime() + nanos : 0L;
            //当前请求节点的线程..
            Thread w = Thread.currentThread();
            //允许自旋检查的次数..
            int spins = ((head.next == s) ?
                         (timed ? maxTimedSpins : maxUntimedSpins) : 0);

            //自旋:1.检查状态等待匹配  2.挂起线程  3.检查状态 是否被中断 或者 超时..
            for (;;) {
                //条件成立:说明线程等待过程中,收到了中断信号,属于中断唤醒..
                if (w.isInterrupted())
                    //更新线程对应的Node状态为 取消状态..
                    //数据域item 指向当前Node自身,表示取消状态.
                    s.tryCancel(e);


                //获取当前Node数据域
                Object x = s.item;
                //item有几种情况呢?
                //当SNode模式为DATA模式时:
                //1.item != null 且 item != this  表示请求要传递的数据 put(E e)
                //2.item == this 当前SNode对应的线程 取消状态
                //3.item == null 表示已经有匹配节点了,并且匹配节点拿走了item数据。

                //当SNode模式为REQUEST模式时:
                //1.item == null 时,正常状态,当前请求仍然未匹配到对应的DATA请求。
                //2.item == this 当前SNode对应的线程 取消状态
                //3.item != null 且 item != this  表示当前REQUEST类型的Node已经匹配到一个DATA类型的Node了。



                //条件成立:
                //当前请求为DATA模式时:e 请求带来的数据
                //item == this 当前SNode对应的线程 取消状态
                //item == null 表示已经有匹配节点了,并且匹配节点拿走了item数据。

                //当前请求为REQUEST模式时:e == null
                //item == this 当前SNode对应的线程 取消状态
                //item != null 且 item != this  表示当前REQUEST类型的Node已经匹配到一个DATA类型的Node了。
                if (x != e)
                    return x;

                //条件成立:说明请求指定了超时限制..
                if (timed) {
                    //nanos表示距离截止时间的长度..
                    nanos = deadline - System.nanoTime();
                    //条件成立:说明当前Node对应的线程 已经等待超时了,需要取消了.
                    if (nanos <= 0L) {
                        s.tryCancel(e);
                        continue;
                    }
                }



                //条件成立:说明当前线程 还可以进行自旋检查..
                if (spins > 0)
                    //递减..
                    --spins;


                //执行到这里,说明spins == 0;
                //条件成立:当前Node尚未设置waiter字段..
                else if (s.waiter == null)
                    //保存当前Node对应的线程,方便后面挂起线程后,外部线程使用s.waiter字段唤醒 当前Node对应的线程。
                    s.waiter = w;

                //条件成立:说明当前请求未指定超时限制。挂起采用 不指定超时的挂起方法..
                else if (!timed)
                    LockSupport.park(this);

                //执行到这里,说明 timed==true
                //条件 不成立:nanos 太小了,没有必要挂起线程了,还不如自旋 实在。
                else if (nanos > spinForTimeoutThreshold)
                    //nanos > 1000.
                    LockSupport.parkNanos(this, nanos);
            }
        }
案例分析
public class TransferQueueDemo01 {
    public static void main(String[] args) throws InterruptedException {
        SynchronousQueue<Integer> queue = new SynchronousQueue<>(true);
        Thread thread1 = new Thread(() -> {
            try {
                queue.put(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread1.start();

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

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

    }
}

队首节点取消出队分析
public class TransferQueueCancelDemo01 {
    /**
     * head.next节点取消....案例
     */
    public static void main(String[] args) throws InterruptedException {
        SynchronousQueue<Integer> queue = new SynchronousQueue<>(true);

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

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

        Thread.sleep(100);
        thread1.interrupt();

    }
}

队中节点取消出队分析
public class  TransferQueueCancelDemo02 {
    public static void main(String[] args) throws InterruptedException {
        SynchronousQueue<Integer> queue = new SynchronousQueue<>(true);

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

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

        Thread.sleep(200);
        Thread thread3 = new Thread(() -> {
            try {
                queue.put(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread3.start();

        Thread.sleep(200);
        thread2.interrupt();
    }
}

队尾节点取消出队分析
public class TransferQueueCancelDemo03 {
    public static void main(String[] args) throws InterruptedException {
        SynchronousQueue<Integer> queue = new SynchronousQueue<>(true);

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

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

        Thread.sleep(200);
        thread2.interrupt();

        Thread.sleep(200);

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

        Thread.sleep(200);
        thread3.interrupt();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值