ConcurrentLinkedQueue中非阻塞链表的插入

 在许多队列算法中,空队列通常都包含一个“哨兵(Sentinel)”或者“哑(Dummy)节点”,并且头结点和尾节点在初始化时都指向该哨兵节点。尾节点通常要么指向哨兵节点(如果队列为空),即队列的最后一个元素,要么(当有操作正在执行更新时)指向倒数第二个元素。

使用哨兵节点与不使用哨兵节点的区别:
 带哨兵节点的链表,需要额外的一个节点,插入或删除时,不论操作的位置,表头都不变,不需要额外的判断;不带哨兵节点的链表,插入或删除操作发生在第一个节点时,表头指针都要变化,需要额外的处理,在处理链表为空时,和其他情况不一样,需要单独判断一次。

@ThreadSafe
public class LinkedQueue <E> {

    private static class Node <E> {
        final E item;
        final AtomicReference<Node<E>> next;

        public Node(E item, Node<E> next) {
            this.item = item;
            this.next = new AtomicReference<Node<E>>(next);
        }
    }

    private final Node<E> dummy = new Node<E>(null, null);
    private final AtomicReference<Node<E>> head
            = new AtomicReference<Node<E>>(dummy);
    private final AtomicReference<Node<E>> tail
            = new AtomicReference<Node<E>>(dummy);

    public boolean put(E item) {
        Node<E> newNode = new Node<E>(item, null);
        while (true) {
            Node<E> curTail = tail.get();
            Node<E> tailNext = curTail.next.get();
            if (curTail == tail.get()) {
                if (tailNext != null) {
                    // 队列处于中间状态,推进尾节点
                    tail.compareAndSet(curTail, tailNext);
                } else {
                    // 处于稳定状态,尝试插入新节点
                    if (curTail.next.compareAndSet(null, newNode)) {
                        // 插入操作成功,尝试推进尾节点
                        tail.compareAndSet(curTail, newNode);
                        return true;
                    }
                }
            }
        }
    }
}

稳定的两个节点队列:
稳定的两个节点队列
插入中的三个节点:
插入中的三个节点
插入完成的三个节点:
插入完成的三个节点

猜你喜欢

转载自blog.csdn.net/u014653854/article/details/80728276
今日推荐