Java并发工具:ConcurrentLinkedQueue
ConcurrentLinkedQueue 是 Java 并发包中基于无锁算法实现的高性能线程安全无界队列,属于 java.util.concurrent 包。它基于链表结构实现,并且适用于高并发场景下的先进先出(FIFO)操作。
核心特性
- 无锁设计
采用 CAS(Compare-And-Swap)操作实现线程安全,避免了传统锁机制的性能瓶颈,减少线程阻塞。
- 非阻塞队列
所有操作(如入队、出队)均不会因锁竞争导致线程挂起,适合高吞吐量场景。
- 无界队列
动态扩展链表节点,理论上容量仅受内存限制。
- FIFO 顺序
遵循先进先出原则,元素从头部移除、尾部插入。
- 单向链表结构
内部由 Node 节点构成单向链表,头尾节点(head、tail)通过 volatile 修饰保证可见性。
实现原理
- 数据结构
每个 Node 包含 volatile 修饰的 item(元素)和 next(后继节点指针)。
初始化时头尾节点指向同一个空节点,后续操作中头尾更新可能延迟。
- 入队(offer 方法)
CAS 操作尾部插入:新元素追加到链表尾部,通过 CAS 更新尾节点 tail。若 CAS 失败则重试,直至成功。
延迟更新尾节点:并非每次插入都更新 tail,减少 CAS 竞争,提升性能。
- 出队(poll 方法)
CAS 操作头部移除:从头部节点获取元素,通过 CAS 将头节点 item 置为 null,并移动 head 指针。
协助清理逻辑:若发现其他线程未完成的出队操作,会协助完成以维持队列状态一致性。
常用方法
add(E e) | 将元素插入队列尾部,成功返回 true,如果由于容量限制导致失败则抛出异常(但因为是无界队列,通常不会失败)。 |
offer(E e) | 将元素插入队列尾部,总是返回 true(因为是无界队列)。 |
poll() | 获取并移除队列头部元素;如果队列为空,则返回 null。 |
peek() | 获取但不移除队列头部元素;如果队列为空,则返回 null。 |
size() | 返回当前队列中的元素数量(注意:不是实时精确值)。 |
isEmpty() | 判断队列是否为空(弱一致性判断)。 |
与其他队列对比
队列类型 | 是否阻塞 | 是否线程安全 | 是否支持 null | 特点 |
ConcurrentLinkedQueue | 否 | 是 | 否 | 非阻塞、高性能、弱一致性 |
LinkedList | 否 | 否 | 是 | 单线程适用 |
ArrayBlockingQueue | 是 | 是 | 否 | 有界、阻塞、性能略低 |
LinkedBlockingQueue | 是 | 是 | 否 | 可配置阻塞行为 |
PriorityQueue | 否 | 否 | 是 | 优先级排序,非线程安全 |
DelayQueue | 是 | 是 | 否 | 延迟获取元素 |
适用场景
- 高并发读写
如电商系统中的购物车操作、实时日志处理等,需保证数据一致性与低延迟。
- 无界数据流转
适用于生产者速率不可控但需高效处理的场景(如消息队列中间件)。
注意事项
- 遍历弱一致性
迭代器可能反映队列某一时刻的快照,不保证实时性。
- 慎用 size() 方法
遍历链表计算元素数量,时间复杂度为 O(n),高并发下性能较差。
- 不能放入 null 值
如果调用 offer(null) 或 add(null),会抛出 NullPointerException。