并发编程--ConcurrentLinkedQueue详解

一、简介:

        工作中有时 候需要使用 线 程安全的 列。如果要 实现 一个 线 程安全的 列有两种方式:一种是使用阻塞算法,另一种是使用非阻塞算法。使用阻塞算法的队 列可以用一个 锁 (入队 和出 用同一把 )或两个 (入 和出 用不同的 )等方式来 实现 。非阻塞的 实现 方式则 可以使用循 CAS 的方式来 实现
ConcurrentLinkedQueue 就是juc包中自带的经典非堵塞方式实现的工具类

二、结构

并发编程--ConcurrentLinkedQueue详解_第1张图片

 

ConcurrentLinkedQueue head 点和 tail 成,每个 点( Node )由 点元素( item )和
指向下一个 点( next )的引用 成, 点与 点之 就是通 过这 next 起来,从而 成一
张链 构的 列。默 情况下 head 点存 的元素 空, tail 点等于 head 点。
private transient volatile Node tail = head;

三、入队

从源代 角度来看,整个入 队过 程主要做两件事情:第一是定位出尾 点;第二是使用
CAS 算法将入 队节 置成尾 点的 next 点,如不成功
    public boolean offer(E e) {
        checkNotNull(e);
        // 入队前,创建一个入队节点
        final Node newNode = new Node(e);

        for (Node t = tail, p = t;;) {
            // 创建一个指向tail节点的引用
            Node q = p.next;
            if (q == null) {
                // p is last node
                if (p.casNext(null, newNode)) {
                    // Successful CAS is the linearization point
                    // for e to become an element of this queue,
                    // and for newNode to become "live".
                    if (p != t) // hop two nodes at a time
                        casTail(t, newNode);  // Failure is OK.
                    return true;
                }
                // Lost CAS race to another thread; re-read next
            }
            else if (p == q)
                // We have fallen off list.  If tail is unchanged, it
                // will also be off-list, in which case we need to
                // jump to head, from which all live nodes are always
                // reachable.  Else the new tail is a better bet.
                p = (t != (t = tail)) ? t : head;
            else
                // Check for tail updates after two hops.
                p = (p != t && t != (t = tail)) ? t : q;
        }
    }
tail 点并不 是尾 点,所以每次入 都必 先通 tail 点来找到尾 点。尾 点可能
tail 点,也可能是 tail 点的 next 点。代 中循 体中的第一个 if 就是判断 tail 是否有 next
点,有 表示 next 点可能是尾 点。 tail 点的 next 点需要注意的是 p 点等于 p next
点的情况,只有一种可能就是 p 点和 p next 点都等于空,表示 初始化,正准 添加 点,所以需要返回 head 点。
/**
*返回 p 的后继节点,或者如果 p.next 已经链接到 self 则返回头节点,这只有在使用现在不在列表*中的陈旧指针遍历时才会为真。 
**/
   final Node succ(Node p) {
        Node next = p.next;
        return (p == next) ? head : next;
    }

四、出列

public E poll() {
        restartFromHead:
        for (;;) {
            for (Node h = head, p = h, q;;) {
                //入列折腾的tail,那出列折腾的就是head
                E item = p.item;
                //出列判断依据是节点的item=null
                //item != null, 并且能将操作节点的item设置null, 表示出列成功
                if (item != null && p.casItem(item, null)) {
                    if (p != h) 
                        //一旦出列成功需要对head进行移动
                        updateHead(h, ((q = p.next) != null) ? q : p);
                    return item;
                }
                else if ((q = p.next) == null) {
                    updateHead(h, p);
                    return null;
                }
                else if (p == q)
                    //第一轮操作失败,下一轮继续,调回到循环前
                    continue restartFromHead;
                else
                    //推动head节点移动
                    p = q;
            }
        }
    }

五、ConcurrentLinkedQueue使用特点

ConcurrentLinkedQueue使用约定:
1:不允许null入列
2:在入队的最后一个元素的next为null
3:队列中所有未删除的节点的item都不能为null且都能从head节点遍历到
4:删除节点是将item设置为null, 队列迭代时跳过item为null节点
5:head节点跟tail不一定指向头节点或尾节点,可能存在滞后性

你可能感兴趣的:(并发编程,java,高并发)