java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java ConcurrentLinkedQueue

Java中的ConcurrentLinkedQueue的使用小结

作者:有梦想的攻城狮

ConcurrentLinkedQueue是Java并发包中的无界非阻塞线程安全队列,本文主要介绍了ConcurrentLinkedQueue的使用,具有一定的参考价值,感兴趣的可以了解一下

一、概述

ConcurrentLinkedQueue 是 Java 并发包(java.util.concurrent)中提供的无界非阻塞线程安全队列,基于单向链表实现,采用 CAS(Compare-and-Swap) 操作和 无锁算法 保证并发安全。其核心设计目标是高吞吐量低延迟,适用于高并发场景下的生产者-消费者模型。

关键特性

  1. 无界性:理论上容量无限,但受内存限制。
  2. 非阻塞:操作永不阻塞线程,失败立即返回(如 poll() 在队列为空时返回 null)。
  3. 无锁设计:通过 CAS 和自旋机制实现线程安全,避免传统锁的开销。
  4. FIFO 顺序:严格遵循先进先出原则。
  5. 弱一致性迭代器:遍历时可能看到部分更新,但不会抛出 ConcurrentModificationException

二、内部数据结构

1. 节点类Node<E>

private static class Node<E> {
    volatile E item;          // 存储元素
    volatile Node<E> next;    // 指向下一个节点
    // CAS 操作方法
    boolean casItem(E cmp, E val) { ... }
    boolean casNext(Node<E> cmp, Node<E> val) { ... }
}

2. 队列指针

三、核心方法与实现原理

1.入队操作(offer/add)

public boolean offer(E e) {
    checkNotNull(e);
    Node<E> newNode = new Node<>(e);
    for (Node<E> t = tail, p = t;;) {
        Node<E> q = p.next;
        if (q == null) {  // p 是尾节点
            if (p.casNext(null, newNode)) {
                if (p != t) casTail(t, newNode);  // 更新 tail
                return true;
            }
        } else if (p == q) {  // 自引用节点,重置 tail
            p = (t != (t = tail)) ? t : head;
        } else {
            p = (p != t && t != (t = tail)) ? t : q;
        }
    }
}

2.出队操作(poll)

public E poll() {
    restartFromHead:
    for (;;) {
        for (Node<E> h = head, p = h, q;;) {
            E item = p.item;
            if (item != null && p.casItem(item, null)) {
                if (p != h) updateHead(h, p);  // 更新 head
                return item;
            } else if ((q = p.next) == null) {
                updateHead(h, p);
                return null;
            } else if (p == q) continue restartFromHead;
            else p = q;
        }
    }
}

3.其他方法

四、无锁并发控制机制

1. CAS 操作

2. 松弛不变量(Relaxed Invariants)

3. 自引用节点

五、适用场景

  1. 高并发生产者-消费者模型:如日志处理、实时事件分发。
  2. 低延迟系统:如高频交易、游戏服务器。
  3. 无界缓冲需求:需动态扩展队列长度的场景。
  4. 弱一致性要求:允许短暂的数据不一致(如遍历时)。

与LinkedBlockingQueue对比

特性ConcurrentLinkedQueueLinkedBlockingQueue
线程安全机制CAS 无锁锁(ReentrantLock)
容量限制无界可选有界/无界
阻塞操作支持 put()/take()
吞吐量中等
内存占用节点结构更轻量可能更高

六、最佳实践

  1. 避免频繁调用 size():高并发下性能差,建议通过外部计数器统计。
  2. 合理预估容量:虽无界,但内存耗尽可能引发 OOM。
  3. 结合其他同步机制:如需精确控制,可与 Semaphore 或 CountDownLatch 联用。
  4. 弱一致性遍历:接受遍历时可能遗漏新元素,适用于非强一致性场景。

七、源码设计细节

  1. 哨兵节点:初始化时 head 和 tail 指向同一个哨兵节点,简化边界条件处理。
  2. 自引用节点:出队后原头节点 next=self,标记为待回收。
  3. CAS 优化:节点构造时使用 Unsafe.putObject 替代 volatile 写操作,减少内存屏障开销。

八、总结

ConcurrentLinkedQueue 是 Java 并发编程中高性能无锁队列的典范,通过 CAS 和松弛不变量设计,在保证线程安全的同时最大化吞吐量。适用于对延迟敏感、无需严格容量控制的场景,但需注意其无界特性可能带来的内存风险。理解其底层机制(如 CAS、自旋、自引用节点)有助于在实际工程中合理应用。

到此这篇关于Java中的ConcurrentLinkedQueue的使用小结的文章就介绍到这了,更多相关Java ConcurrentLinkedQueue内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文