ConcurrentLinkedQueue是基于单向链表的无界非阻塞队列,使用CAS实现线程安全;remove(Object o)从头节点单向遍历,通过equals匹配并CAS跳过首个匹配节点,无prev指针、不支持双向操作。

ConcurrentLinkedQueue 的 remove(Object o) 方法确实存在,但它不是基于双向链表匹配,也不依赖“双向链表中的 CAS 比较”——这个前提本身不成立。理解这一点是避免设计误判的关键。
remove(Object) 的实际实现机制
该方法会从头节点开始顺序遍历整个队列(单向),对每个节点的元素调用 equals() 判断是否匹配目标对象。一旦找到匹配节点,就尝试通过 CAS 原子地将其前驱节点的 next 指针跳过该节点(即逻辑删除)。整个过程不涉及 prev 指针、不回溯、不双向扫描。
- 遍历是单向的:仅靠 head → node.next → ... 一路向下
- CAS 操作只作用于前驱节点的
next字段,使其指向待删节点的后继 - 没有 prev 字段,因此无法“从尾向前匹配”或“双向同步定位”
- 若遍历时遇到已标记为删除的中间节点(如因其他线程调用 poll() 导致的“哨兵节点”),会触发帮助清理(helpDelete)
为什么不存在“双向链表匹配中的 CAS”
ConcurrentLinkedQueue 的 Node 类定义中只有 item 和 next 两个 volatile 字段,没有 prev 字段。JDK 源码(如 JDK 8/11/17)均可验证:
private static class Node<E> {
volatile E item;
volatile Node<E> next;
// ⚠️ 无 prev 字段
}
- 双向链表结构常见于
ConcurrentLinkedDeque(双端队列),而非ConcurrentLinkedQueue - remove() 的线性扫描 + 单向 CAS 跳过,是为兼顾正确性与无锁性能做的取舍;它不保证 O(1) 删除,最坏 O(n)
- 若需高效按值删除,应考虑是否真的需要无界并发队列——有时
CopyOnWriteArrayList或加锁的LinkedBlockingQueue更合适
实际使用中的关键注意事项
调用 remove(o) 时需清楚其代价和语义:
立即学习“Java免费学习笔记(深入)”;
- 不是原子性“查找+删除”操作:查找过程可能被其他线程的入队/出队干扰,遍历途中节点可能被修改
- 不阻塞,但可能因长队列导致显著延迟;高并发下频繁调用会影响吞吐量
- 仅删除第一个匹配项(即使有重复元素),且不保证与其他操作的实时一致性(如另一线程刚入队相同元素,可能刚好错过)
- 元素
equals()实现必须合理;若为 null,用 == 判断;若未重写 equals,将依赖引用相等
替代方案建议
若业务强依赖按值快速删除,可考虑:
- 用
ConcurrentHashMap<K, Boolean>+ 独立队列做存在性索引(空间换时间) - 选用
ConcurrentLinkedDeque(双向链表,但 remove 仍是单向遍历,只是支持头尾操作) - 在低并发场景下,用
ArrayBlockingQueue或LinkedBlockingQueue配合外部同步 - 自定义带哈希索引的并发容器(需谨慎处理 ABA 及内存可见性)


















