concurrentlinkedqueue是java并发包中基于cas实现的无锁、非阻塞、无界线程安全队列,适用于高并发低延迟场景;它通过volatile字段和unsafe原子操作实现offer/poll,不支持null,size()开销大,迭代器弱一致。

ConcurrentLinkedQueue 是 Java 并发包中典型的无锁(lock-free)非阻塞队列,基于 CAS(Compare-and-Swap)实现,适用于高并发、低延迟场景。它不加锁、不阻塞线程,适合生产者-消费者模型中对吞吐量敏感、但不严格要求强一致性的场景。
核心原理:无锁 + CAS + 原子操作
ConcurrentLinkedQueue 内部使用单向链表节点(Node),每个节点包含 item 和 next 字段。所有修改操作(offer/poll)都通过 Unsafe 类的 CAS 原子指令完成:
- 插入(offer):在队尾 CAS 更新 tail 指针,若失败则重试;必要时跳过中间已出队节点,寻找最新合法 tail
- 删除(poll):在队头 CAS 更新 head 指针并“摘下”节点;若 head 节点为空(已被其他线程取走),则推进 head 直到找到有效节点
- 没有全局锁或 synchronized,也没有 AQS 队列,线程不会挂起,避免上下文切换开销
典型用法与注意事项
直接使用 offer() 和 poll() 即可,但需注意以下关键点:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 不支持 null 元素:插入 null 会抛 NullPointerException,务必提前校验
- 弱一致性迭代器:iterator() 返回的迭代器不保证反映实时状态,遍历时可能跳过新入队元素,也不抛 ConcurrentModificationException
- size() 方法代价高:需遍历整个链表计数,高并发下结果可能瞬间过期,建议避免在性能关键路径调用
- 不提供阻塞能力:没有 put/take 等阻塞方法,如需等待,应配合 CountDownLatch、LockSupport 或上层轮询+yield/sleep 控制
高性能实践建议
要真正发挥其非阻塞优势,需配合合理的使用模式:
- 批量处理优于逐个操作:例如用 ThreadLocal 缓存临时队列,定期批量 offer 到 ConcurrentLinkedQueue,减少 CAS 竞争
- 避免在 poll 后做耗时操作:否则会拖慢其他线程的 poll 进度;可先快速取出,再异步处理
- 结合 volatile 标志位控制生命周期:例如用 volatile boolean running 控制生产者是否继续 offer,比依赖队列空/满更轻量
- 监控实际竞争程度:可通过 JMH 测量不同线程数下的吞吐量拐点,或用 VisualVM 观察 Unsafe.park/unpark 频率(理想情况下应极少)
对比其他并发队列的适用边界
不是所有场景都适合 ConcurrentLinkedQueue:
- 需要阻塞等待?→ 选 LinkedBlockingQueue 或 ArrayBlockingQueue
- 要求绝对 FIFO 且强顺序可见性?→ 它满足 FIFO,但因无内存屏障强制刷新,极端情况下可能因 CPU 重排序影响感知顺序(通常不影响业务逻辑)
- 元素数量固定且可控?→ 若容量明确,ArrayBlockingQueue 的缓存局部性更好,CAS 竞争更低
- 需优先级调度?→ ConcurrentLinkedQueue 不支持,改用 PriorityBlockingQueue(但它是阻塞且基于锁的)
它最适合的场景是:大量线程高频写入、读取频率相近、能容忍短暂不可见、且不允许线程阻塞的系统,比如日志采集缓冲、事件总线中转、实时指标聚合管道等。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










