concurrentlinkeddeque 是基于无锁 cas 和 volatile 的线程安全双向非阻塞队列,支持 addfirst/addlast/removefirst/removelast 等操作,无全局锁、不阻塞、不保证 size 精确性,适用于高并发低延迟场景。

ConcurrentLinkedDeque 是 Java 并发包(java.util.concurrent)中一个线程安全的双向非阻塞队列,它基于无锁(lock-free)算法实现,底层使用 CAS(Compare-And-Swap)原子操作配合 volatile 变量来保证多线程环境下的正确性与高性能。
核心设计:无锁 + CAS + volatile 链表节点
它内部维护一个双向链表结构,每个节点(Node)包含 item、prev 和 next 引用。所有字段均为 volatile,确保可见性;所有修改(如插入、删除)都通过 UNSAFE.compareAndSetXXX 完成,避免锁竞争。
- 头尾指针(
head和tail)也是 volatile 的,且会动态更新以维持“近似”最优位置,不严格要求始终指向真实首尾,而是允许一定滞后,提升并发效率 - 插入/删除操作在头或尾进行时,先尝试 CAS 更新指针;失败则重试,直到成功或发现结构已变(如被其他线程修改),再重新定位
- 没有全局锁或 ReentrantLock,也没有阻塞等待机制——操作要么立即成功,要么重试,符合“非阻塞”定义
双向操作支持:addFirst/addLast/removeFirst/removeLast
所有双端操作都遵循相同无锁逻辑,区别仅在于操作方向和指针更新目标:
-
addFirst(e):在头节点前插入新节点,CAS 更新head指针 -
addLast(e):在尾节点后插入,CAS 更新tail指针 -
removeFirst():尝试移除头节点,需处理空队列、中间节点跳过、以及 head 滞后修正等边界情况 -
removeLast():同理,但操作 tail 和 prev 方向
这些方法不会阻塞,也不抛出异常(空队列调用 removeXxx() 返回 null),适合高吞吐低延迟场景。
内存一致性与 ABA 问题应对
虽然使用 CAS,但 ConcurrentLinkedDeque 并未引入版本号(如 AtomicStampedReference)来彻底解决 ABA 问题。它的策略是:
- 依赖节点不可变性:一旦节点的
item被设为null(表示已删除),该节点就不会再被当作有效数据节点参与 CAS - 通过多次检查前后节点状态(如 pred.next == node && node.next != null)来规避误判,确保逻辑上“当前节点仍处于活跃链路中”
- volatile 读写保障了节点引用和字段值的传播顺序,满足 JMM 的 happens-before 规则
适用场景与注意事项
它适合大量线程频繁在两端做插入/删除、且不能接受阻塞或锁开销的场景,比如工作窃取线程池(ForkJoinPool 内部就类似思想)或事件缓冲区。
- 不支持
size()的精确计数(遍历链表有竞态,返回的是快照值) - 不提供阻塞操作(如
takeFirst()等待),如有等待需求应选LinkedBlockingDeque - 迭代器弱一致性:遍历时可能看不到最新添加的元素,也不会抛
ConcurrentModificationException
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











