ConcurrentLinkedQueue有感

·  阅读 118

来看一下线程安全的队列无界队列ConcurrentLinkedQueue 先来看一下成员变量和构造函数吧

 private static class Node<E> {
        volatile E item;
        volatile Node<E> next
 }
  private transient volatile Node<E> head;
  private transient volatile Node<E> tail;
  
  public ConcurrentLinkedQueue() {
        head = tail = new Node<E>(null);
    }
复制代码

一个内部类Node,只有next指针,说明是单向链表 一个头指针,一个尾指针 构造方法会生成一个空Node,head和tail指针都指向空Node

假设queue.offer("a"),来看看是如何入队的

public boolean offer(E e) {
        checkNotNull(e);
        final Node<E> newNode = new Node<E>(e);
        for (Node<E> t = tail, p = t;;) {
            Node<E> q = p.next;
            if (q == null) {
                if (p.casNext(null, newNode)) {
                    if (p != t) // hop two nodes at a time
                        casTail(t, newNode);  // Failure is OK.
                    return true;
                }
            }
            else if (p == q)
                p = (t != (t = tail)) ? t : head;
            else
                p = (p != t && t != (t = tail)) ? t : q;
        }
    }
复制代码

又是一个无限for循环,这个套路已经很常见了。 首先为元素a生成一个Node,然后把t指向tail,p指向t,此时p,t,head,tail都指向了空Node。q又指向p的next,p的next是空,即q==null。p.casNext(null, newNode)哟,这里用了CAS诶,把node a放到空Node的后面。看来是用CAS来保证插入的线程安全,现在p和t还都指向空Node,所以直接返回true,插入成功

此时又queue.offer("b"): q=p.next=Node a,q!=null,p!=q,走p = (p != t && t != (t = tail)) ? t : q;此时p=t还都指向着空Node,false即p=q都指向Node a。进去下一次for循环,p=q.next,p==null,CAS把Node b插入到Node a后面,并将tail指针指向Node b,返回true

看完了入队,再来看看出队 queue.poll()

public E poll() {
        restartFromHead:
        for (;;) {
            for (Node<E> h = head, p = h, q;;) {
                E item = p.item;
                if (item != null && p.casItem(item, null)) {
                    if (p != h) // hop two nodes at a time
                        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
                    p = q;
            }
        }
    }
复制代码

只要就是CAS把头结点Node a的item置为null,把head指针从指向Node null,移除空Node,返回item "a"

趁热打铁来看看queue.size()

public int size() {
        int count = 0;
        for (Node<E> p = first(); p != null; p = succ(p))
            if (p.item != null)
                // Collection.size() spec says to max out
                if (++count == Integer.MAX_VALUE)
                    break;
        return count;
    }
复制代码

这边只是直接遍历了一下,累加得出了size。在执行size()时,刚好有入队操作呢,是不是就会存在数据一致性问题了呢

分类:
阅读
标签:
分类:
阅读
标签: