Java中的ConcurrentLinkedQueue的使用小结

 更新时间:2026年07月07日 09:19:59   作者:有梦想的攻城狮  
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) { ... }
}
  • volatile 修饰:保证多线程可见性。
  • CAS 操作:通过 Unsafe 类实现原子性更新。

2. 队列指针

  • head:指向队列头部(可能滞后于实际头节点)。
  • tail:指向队列尾部(可能滞后于实际尾节点)。
  • 哨兵节点:初始化时 headtail 均指向一个 item=null 的哨兵节点。

三、核心方法与实现原理

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;
        }
    }
}
  • CAS 竞争:多个线程可能同时尝试插入,仅一个成功。
  • tail 滞后更新:仅在 p != t 时更新 tail,减少 CAS 操作频率。

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;
        }
    }
}
  • CAS 移除:将头节点的 item 设为 null,延迟物理删除。
  • head 更新:若 p != h,则更新 head 指针。

3.其他方法

  • peek():获取头元素但不移除,逻辑与 poll() 类似,不修改 item
  • size():遍历链表统计元素数,非线程安全(高并发下结果可能不准确)。
  • remove(Object o):遍历链表移除首个匹配元素,返回是否成功。

四、无锁并发控制机制

1. CAS 操作

  • 原子性更新:通过 Unsafe.compareAndSwapObject 实现对 itemnext 的原子修改。
  • 自旋重试:CAS 失败时循环重试,而非阻塞线程。

2. 松弛不变量(Relaxed Invariants)

  • head 滞后:可能指向已删除节点,仅在必要时更新(如遍历时遇到自引用节点)。
  • tail 滞后:减少更新频率,提升吞吐量。

3. 自引用节点

  • 标记删除:出队后,原头节点的 next 指向自身,防止其他线程误用。
  • 垃圾回收:物理删除由 GC 处理,避免频繁内存操作。

五、适用场景

  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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java DelayQueue实现延时任务的示例详解

    Java DelayQueue实现延时任务的示例详解

    DelayQueue是一个无界的BlockingQueue的实现类,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。本文就来利用DelayQueue实现延时任务,感兴趣的可以了解一下
    2022-08-08
  • 如何巧用HashMap一行代码统计单词出现次数详解

    如何巧用HashMap一行代码统计单词出现次数详解

    这篇文章主要给大家介绍了关于如何巧用HashMap一行代码统计单词出现次数的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2020-07-07
  • 关于Java整合RocketMQ实现生产消费详解

    关于Java整合RocketMQ实现生产消费详解

    这篇文章主要介绍了关于Java整合RocketMQ实现生产消费详解,RocketMQ作为一款纯java、分布式、队列模型的开源消息中间件,支持事务消息、顺序消息、批量消息、定时消息、消息回溯等,需要的朋友可以参考下
    2023-05-05
  • Java实现的可选择及拖拽图片的面板功能【基于swing组件】

    Java实现的可选择及拖拽图片的面板功能【基于swing组件】

    这篇文章主要介绍了Java实现的可选择及拖拽图片的面板功能,涉及java基于swing组件选择与操作图片元素的相关实现技巧,需要的朋友可以参考下
    2018-01-01
  • Java Socket编程服务器响应客户端实例代码

    Java Socket编程服务器响应客户端实例代码

    这篇文章主要介绍了Java Socket编程服务器响应客户端实例代码,具有一定借鉴价值,需要的朋友可以参考下
    2017-12-12
  • IDEA如何实现远程断点调试jar包

    IDEA如何实现远程断点调试jar包

    这篇文章主要介绍了IDEA如何实现远程断点调试jar包的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-06-06
  • 使用maven整合Spring+SpringMVC+Mybatis框架详细步骤(图文)

    使用maven整合Spring+SpringMVC+Mybatis框架详细步骤(图文)

    这篇文章主要介绍了使用maven整合Spring+SpringMVC+Mybatis框架详细步骤(图文),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-05-05
  • Java webservice的POST和GET请求调用方式

    Java webservice的POST和GET请求调用方式

    这篇文章主要介绍了Java webservice的POST和GET请求调用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • java网络编程之群聊功能

    java网络编程之群聊功能

    这篇文章主要为大家详细介绍了java网络编程之群聊功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • Java实现统计文件夹下所有文件的字数

    Java实现统计文件夹下所有文件的字数

    这篇文章主要为大家详细介绍了如何使用Java实现统计文件夹下所有文件的字数,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-03-03

最新评论