可以看到ConcurrentLinkedDeque实现的接口有Serializable, Iterable<E>, Collection<E>, Deque<E>, Queue<E>,相应的就有各个接口实现的特性,这个类比较突出的特性可能是Concurrent这个词啦。
基于链接节点的无界ConcurrentLinkedDeque(并发双端队列)。并发插入,删除和访问操作可跨多个线程安全执行。当许多线程共享对公共集合的访问权时,ConcurrentLinkedDeque是一个合适的选择。与大多数其他并发集合实现一样,此类不允许使用null元素。
其中Iterators 和 spliterators 是弱一致的。
主要有两个属性字段,head表示队列的队首元素,tail表示队尾。
在网上copy一个图:
ConcurrentLinkedDeque的构造函数只有两个
ConcurrentLinkedDeque当然可以用来当queue和stack实现来用,但是我们主要来看看deque的接口实现,前后两种方法实现的方式一样的,但是返回值会有区分。
队首入队 | addFirst(e) | offerFirst(e) |
队首出队 | removeFirst() | pollFirst() |
队首读取 | getFirst() | peekFirst() |
队尾入队 | addLast(e) | offerLast(e) |
队尾出队 | removeLast() | pollLast() |
队尾读取 | getLast() | peekLast() |
队首入队,用的都是private void linkFirst(E e) ,这里会先创建一个newNode,然后使用CAS方式入队,这里感到奇怪的p = h,q这个写法,那么这是只是定义一个名字,然后告诉他你是什么类型?这好有迷惑性呀。队尾入队也是类似的,我想这个算法极端情况的效率怎么样呢?
让我们看看出队的操作吧,然后执行unlink操作,删除元素,里面的操作还是有点复杂的。队尾的操作也类似。
读取操作比上面的操作应该简单一些吧,其中有个succ操作来获取队首元素。
请注意,与大多数集合不同,size方法不是恒定时间操作。由于这些deques的异步性质,确定元素的当前数量需要遍历元素,因此如果在遍历期间修改此集合,则可能报告不准确的结果。备注里面还写着在并发操作中,可能这个方法没什么用。不过这里p ==(p=p.next)是什么情况?
添加,删除或检查多个元素的批量操作,例如addAll(java.util.Collection <?extends E>),removeIf(java.util.function.Predicate <?super E>)或forEach(java.util。function.Consumer <?super E>),不保证以原子方式执行。例如,与addAll操作并发的forEach遍历可能只会观察到一些添加的元素。
参考:
https://www.jianshu.com/p/602b3240afaf
https://blog.csdn.net/chenleixing/article/details/44143641
https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/ConcurrentLinkedDeque.html
https://segmentfault.com/a/1190000016284649