ConcurrentLinkedQueue总结

1、offer的多线程并发安全

if (p.casNext(null, newNode)) {

上面这行代码通过CAS操作入队,同一时间只有一个线程可以成功。其他线程入队失败,然后死循环,不断尝试让指针往后挪动到最后一个尾部节点,再次尝试用CAS将自己入队,加入队尾。

2、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;
}

简而言之,就是找出第一个节点,然后一直遍历累计计数。

3、size() 方法的一致性问题

如果在遍历时,有人执行入队或者是出队,会怎么样?
1)入队就是设置尾部节点的next指针(volitale修饰),设置好了后立马就可以看到。
2)遍历到一半时,对头出队,此时时感知不到的。

4、iterator机制

1)CopyOnWriteArrayList的iterator,基于副本快照机制来实现,创建了迭代器后,只会用当时的一个副本快照来完成遍历,数据读不一致问题。
2)ConcurrentLinkedQueue的iterator,基于内部的那个单向链表,再迭代时可能会有出队入队。

5、并发包线程安全性

1)ConcurrentLinkedQueue:写并发没问题,读时比如size和迭代是存在问题的。
2)ConcurrentHashMap:读写都没有问题。

6、总结

ConcurrentLinkedQueue,为了优化多线程并发性能,牺牲掉了一些数据一致性。为了保证写队列的高并发性能,大量的采用CAS无锁化操作,很多读操作,尤其是常见的size,不涉及到任何锁。size,可能你刚拿到size是10,立马队列变成了5。线程安全的集合包,使用时要考虑到多线程并发的数据不一致的问题。很多时候多线程访问内存数据结构,直接用并发包里就可以,如果原生的读写并发无法满足需求,还是要对内存数据结构手动上锁。

发布了104 篇原创文章 · 获赞 5 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/zjuwzp/article/details/103790960
今日推荐