ConcurrentLinkedQueue 是基于 CAS、volatile 和原子引用的无锁线程安全队列,通过分离 head/tail 指针、乐观重试和惰性清理实现高并发低延迟,不依赖锁且 offer/poll 均不阻塞。

ConcurrentLinkedQueue 通过 CAS(Compare-and-Swap)+ 原子引用 + 非阻塞算法实现无锁、线程安全的高性能队列,不依赖 synchronized 或 ReentrantLock,所有操作(offer/poll/peek)都靠底层 Unsafe 的原子操作完成,避免了锁竞争和线程挂起开销。
核心机制:CAS + volatile + 分离 head/tail 节点
队列内部由单向链表节点(Node)组成,每个 Node 包含 item(元素)和 next(指向下一个节点的原子引用)。关键设计包括:
- head 和 tail 是独立的 volatile 原子引用,不强求实时一致 —— tail 可能滞后于实际队尾(允许“松弛”),减少 CAS 冲突;head 同理,可能指向已出队的“逻辑头”,靠后续 clean 操作推进。
- 所有修改都靠 UNSAFE.compareAndSet:例如 offer 时循环尝试 CAS 设置 tail.next;poll 时先 CAS 将 head.item 置为 null(标记已读),再 CAS 推进 head 指针。
- 节点 item 设为 volatile,保证多线程间可见性;next 字段用 AtomicReference,支持无锁链接。
offer 操作:追加到队尾(无锁、乐观重试)
大致流程是:获取当前 tail → 尝试 CAS 设置其 next 指向新节点 → 若失败(tail 已变),则重新读取 tail 并重试;若成功但 tail 落后太多(如距离 ≥2),再 CAS 更新 tail 指针前移。这种“延迟更新 tail”的策略显著降低 CAS 失败率。
例子:两个线程同时 offer,即使都看到同一个 tail,只有一个能成功设置 next;另一个自动重试,无需等待或阻塞。
立即学习“Java免费学习笔记(深入)”;
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
poll 操作:从队头移除(两阶段清除 + 惰性清理)
不是直接删除节点,而是分步进行:
- 第一步:读取 head.next(跳过哨兵头节点),尝试 CAS 将其 item 置为 null(表示“正在出队”);
- 第二步:若 item 清空成功,再 CAS 更新 head 指向该节点,完成逻辑出队;
- 过程中若发现 head.next 为 null(队列空)或 item 已为 null(已被其他线程处理),就返回 null 或继续推进 head。
这种“标记-清除”分离的设计避免了 ABA 问题,也减少了对 head 指针的频繁争抢。
为什么高性能?关键在三点
一是完全无锁,没有 monitor 进入/退出开销;二是写写冲突少(tail 更新宽松)、读写几乎不冲突(head/tail 分离);三是内存局部性好,节点分配连续,且大量使用 CPU 硬件级 CAS,适合现代多核缓存一致性协议。
注意:它不保证强一致性 —— size() 方法需遍历链表,结果可能不准;也不提供阻塞语义(如 put/take),如需阻塞请选择 LinkedBlockingQueue。


















