ConcurrentLinkedQueue是Java并发包中基于单向链表实现的无界非阻塞线程安全队列,采用CAS原子操作和volatile变量保障线程安全,通过head/tail滞后更新与惰性清理实现高吞吐低延迟。

ConcurrentLinkedQueue 是 Java 并发包(java.util.concurrent)中一个线程安全、无界、基于链表实现的队列,它不使用传统的锁(如 synchronized 或 ReentrantLock),而是完全依靠 CAS(Compare-and-Swap)原子操作 和 volatile 变量语义 来保证多线程下的正确性。它的核心在于“无锁”(lock-free),而非“无等待”(wait-free),即总有一个线程能在有限步内完成操作,不会被其他线程阻塞或挂起。
底层是带哨兵节点的单向无锁链表
ConcurrentLinkedQueue 内部维护一个单向链表,每个节点是 Node 类实例:
- 节点包含
item(元素值,可为 null)和next(指向后继节点的 volatile 引用); - 队列始终维持两个关键 volatile 引用:
head和tail,分别指向逻辑上的头节点和尾节点; - 初始时 head 和 tail 都指向同一个“哨兵节点”(dummy node),其 item 为 null,next 指向自身(或 null,取决于版本);
- 实际入队/出队操作并不直接修改 head/tail,而是在它们“滞后”的前提下,通过 CAS 原子更新指针,并允许结构短暂处于“不一致但可修复”的中间状态。
入队(offer)靠 CAS 更新 tail + 两步推进策略
入队不是简单地把新节点连到 tail.next 再更新 tail,而是采用“先连后提”的两阶段方式,避免因 tail 滞后导致的丢失:
- 第一步:获取当前 tail,尝试将新节点设为其 next(CAS tail.next → newNode);若成功,说明 tail 尚未被其他线程更新,进入第二步;若失败(next 已非 null),说明 tail 已滞后,需先推进 tail;
- 第二步:尝试 CAS tail → newNode(即把 tail 指向刚插入的节点);若失败(tail 已被其他线程更新),说明已有线程完成了推进,当前线程无需再推;
- 如果第一步失败(tail.next 不为 null),则先帮助推进 tail(跳过已满的中间节点),再重试插入——这体现了 lock-free 算法中常见的“协助(helping)”思想。
出队(poll)靠 CAS 更新 head + 惰性清理机制
出队也不直接删除节点,而是通过移动 head 指针“跳过”已出队节点,并延迟清理:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
立即学习“Java免费学习笔记(深入)”;
- 读取当前 head 节点及其 item;若 item 非 null,尝试 CAS head.item → null(标记该节点已被消费);成功则返回 item;
- 若 item 已为 null(说明 head 是哨兵或已被其他线程清空),则尝试 CAS head → head.next(跳到下一个真实节点),再重试;
- head 的推进是“惰性的”:即使中间有多个已出队节点,head 也可能长期停留在旧位置,只要能通过 next 链找到下一个有效节点即可;
- 这种设计避免了在出队时同步修改多个指针,也降低了 CAS 失败率,但会带来内存占用略高(已出队节点暂不被 GC)的问题。
关键保障:volatile + CAS + 状态机建模
整个算法的正确性依赖三方面协同:
- volatile 语义:所有对 next、item、head、tail 的读写都具有 happens-before 关系,确保线程间可见性;
- CAS 原子性:所有关键指针更新都用 Unsafe.compareAndSetObject,失败即重试,无锁自旋;
- 节点状态机:每个 Node 的 item 和 next 组合构成有限状态(如:item≠null & next==null 表示有效待消费节点;item==null & next!=null 表示已出队哨兵;next==this 表示终止态),算法只在合法状态间迁移。
ConcurrentLinkedQueue 的无锁设计牺牲了部分内存效率和代码可读性,换来高并发下的低延迟与高吞吐。它不提供阻塞、容量限制或批量操作,适合生产者-消费者速率接近、且对响应时间敏感的场景。

















