concurrentlinkedqueue 是无锁的,因其基于 cas 和原子操作实现,使用 volatile head/tail 引用,所有操作通过 unsafe cas 完成,无阻塞、无锁竞争,冲突时重试而非挂起。

ConcurrentLinkedQueue 是 Java 并发包中典型的无锁(lock-free)线程安全队列,底层基于 CAS(Compare-And-Swap)和原子操作实现,不依赖 synchronized 或 ReentrantLock,适合高并发、低延迟场景。
为什么 ConcurrentLinkedQueue 是无锁的
它内部使用两个 volatile Node 引用(head 和 tail),所有入队(offer)、出队(poll)操作都通过 Unsafe 的 CAS 原子指令完成。没有阻塞、没有锁竞争,线程冲突时靠重试而非挂起,因此不会发生死锁或优先级反转。
注意:它不保证强一致性 —— size() 方法需遍历链表,结果可能滞后;也不支持 BlockingQueue 接口,不能阻塞等待元素。
基本用法:offer 和 poll 是核心操作
日常使用只需关注 offer(E e) 和 poll(),二者都是 O(1) 平均时间复杂度,且线程安全:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- offer(e):将元素插入队尾,总是返回 true(不会失败,也不阻塞)
- poll():取出并移除队首元素;若队列为空,返回 null(不是抛异常)
- peek():只查看队首元素,不移除;空队列也返回 null
示例:
Queuequeue.offer("task1");
queue.offer("task2");
System.out.println(queue.poll()); // 输出 "task1"
适用场景与注意事项
适合生产者-消费者模型中对吞吐量敏感、允许“尽力而为”语义的场景,比如日志缓冲、异步任务分发、事件总线内部队列。
- 不要用 size() 判断是否为空 —— 改用 isEmpty(),因为 size() 可能因并发修改而不准
- 不支持 null 元素 —— offer(null) 会直接抛 NullPointerException
- 迭代器弱一致性:遍历时可能看不到最新入队元素,也可能看到已出队元素,但不会抛 ConcurrentModificationException
- 内存占用略高 —— 每个节点含 next 引用,且出队后节点不会立即被 GC(需等后续 CAS 更新指针)
和 LinkedBlockingQueue 的关键区别
如果需要阻塞能力(如 take() 等待元素)、容量限制、或精确 size 统计,应选 LinkedBlockingQueue;它用可重入锁 + Condition 实现,有锁开销但语义更强。
ConcurrentLinkedQueue 更轻量、更激进地追求吞吐,适合“快进快出”+“大量线程争抢”的典型无锁场景。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










