ConcurrentLinkedQueue 的 size() 方法在高并发下不建议频繁调用,因其需遍历整个链表,时间复杂度为 O(n),且因并发修改可能导致计数不准、跳过或重复节点,甚至触发 ConcurrentModificationException;应使用常量时间、线程安全的 isEmpty() 判断空状态。

ConcurrentLinkedQueue 的 size() 方法在高并发下不建议频繁调用,因为它不是常量时间操作,而是需要遍历整个链表,时间复杂度为 O(n),且结果不保证实时准确。
size() 为什么慢且不准
ConcurrentLinkedQueue 底层是无锁单向链表,size() 实现会从 head 开始逐个遍历节点计数。这个过程:
- 必须扫描全部存活和已逻辑删除但尚未 unlink 的节点;
- 遍历时可能遇到正在被其他线程修改的链表结构(如 tail 更新、节点 unlink),导致部分节点被跳过或重复计数;
- 文档虽未明说,但实际运行中存在因并发修改引发的不可靠行为,甚至极小概率触发
ConcurrentModificationException(尤其在 debug 或监控强校验场景下); - 没有加锁也不依赖 CAS 原子性,纯属“尽力而为”的近似统计。
替代 isEmpty() 判断更高效安全
判断队列是否为空应始终用 isEmpty(),它只比较 head 和 tail 引用是否相等(或检查 head.item 是否为 null),是常量时间、线程安全、无副作用的操作。
- ✅ 正确写法:
if (queue.isEmpty()) { ... } - ❌ 避免写法:
if (queue.size() == 0) { ... }—— 多余遍历 + 结果未必反映瞬时状态
批量消费或循环取数据时别依赖 size()
常见错误模式:用 for (int i = 0; i 取完所有元素。这会导致:
立即学习“Java免费学习笔记(深入)”;
- 每次循环都重新遍历,性能随队列长度线性恶化;
- 并发环境下,两次
size()调用之间可能已有元素入队/出队,造成漏取或重复取; - 极端情况下,若队列持续有新元素加入,可能陷入“永远取不完”的假象。
✅ 推荐做法:用 poll() 循环直到返回 null,例如:
监控类场景需控制采样频率
如果确实需要知道大致规模(如指标上报、告警阈值),可接受弱一致性:
- 避免每次 offer/poll 都调用
size(); - 改用定时任务(如每秒一次)采样并记录日志或发 metrics;
- 结合业务节奏评估误差容忍度——例如秒级监控允许 ±10% 偏差,就不必强求精确。



















