ConcurrentLinkedQueue是Java中基于CAS的无界非阻塞队列,支持高并发FIFO操作,通过弱一致性迭代器实现线程安全,适用于低延迟场景但需注意内存溢出。
一、核心特性
| 特性 | 说明 |
|---|---|
| 无界队列 | 基于链表动态扩展,无固定容量限制。 |
| 非阻塞算法 | 使用 CAS(Compare-And-Swap)实现线程安全,避免锁竞争。 |
| FIFO 顺序 | 元素按插入顺序出队,保证先进先出。 |
| 弱一致性迭代器 | 迭代器遍历时可能不反映最新的修改,但保证不抛出 ConcurrentModificationException。 |
| 高性能并发 | 在低至中度线程竞争下表现优异,适用于高吞吐量场景。 |
二、实现原理
1. 数据结构
-
节点结构:每个节点(
Node)包含元素值(item)和指向下一个节点的指针(next)。private static class Node<E> { volatile E item; volatile Node<E> next; // 构造方法和CAS操作... } -
头尾指针:队列通过
head和tail指针管理元素插入和移除,但不保证tail始终指向最后一个节点(优化插入性能)。
2. 非阻塞算法(CAS)
-
插入(
offer) :- 定位当前尾节点(可能非最新)。
- 通过 CAS 将新节点链接到尾节点的
next指针。 - 更新
tail指针(允许延迟更新,减少 CAS 竞争)。
-
移除(
poll) :- 定位当前头节点。
- 通过 CAS 将头节点的
item置为null(标记为已移除)。 - 更新
head指针到下一个有效节点。
3. ABA 问题处理
- 机制:通过
item的原子更新(置null)避免 ABA 问题,确保节点状态变化的可见性。
4. 弱一致性迭代器
- 实现:迭代器在创建时记录当前链表快照,遍历过程中可能不反映后续修改。
- 影响:适用于不需要实时一致性的场景(如统计、批量处理)。
三、使用场景
| 场景 | 说明 |
|---|---|
| 高并发生产者-消费者 | 多个线程同时插入和移除元素,如任务分发、事件驱动架构。 |
| 无阻塞任务队列 | 需要低延迟响应的场景(如实时日志处理)。 |
| 资源池管理 | 管理可复用的对象(如数据库连接池的闲置连接)。 |
| 高性能消息传递 | 在消息中间件中传递高吞吐量消息。 |
四、代码示例
import java.util.concurrent.ConcurrentLinkedQueue;
public class ConcurrentQueueDemo {
public static void main(String[] args) {
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
// 生产者线程
new Thread(() -> {
for (int i = 0; i < 100; i++) {
queue.offer("Task-" + i);
System.out.println("Produced: Task-" + i);
}
}).start();
// 消费者线程
new Thread(() -> {
while (true) {
String task = queue.poll();
if (task != null) {
System.out.println("Consumed: " + task);
}
}
}).start();
}
}
五、性能对比
| 队列类型 | 锁机制 | 适用场景 | 吞吐量 | 延迟 |
|---|---|---|---|---|
ConcurrentLinkedQueue | 无锁(CAS) | 高并发非阻塞操作 | 高 | 低 |
LinkedBlockingQueue | 双锁分离 | 高吞吐量阻塞队列 | 中高 | 中 |
ArrayBlockingQueue | 单锁 | 固定容量阻塞队列 | 中 | 低 |
六、注意事项
- 内存消耗:无界队列可能因元素堆积导致 OOM,需监控队列长度。
- 迭代器弱一致性:遍历时可能遗漏最新插入或移除的元素。
- CAS 竞争:极端高并发下,频繁 CAS 失败可能降低性能。
七、总结
- 优势:无锁设计带来高吞吐量,适用于高并发非阻塞场景。
- 劣势:无界特性需谨慎使用,弱一致性迭代器不适用强一致性需求。
- 选型建议:在需要低延迟、高并发且容忍弱一致性的场景下优先选择。