CopyOnWriteArrayList 适合读多写少、写操作不频繁的并发场景,如监听器列表、配置缓存、状态快照;读无锁高效,但写操作触发全量数组复制,导致高内存与CPU开销,且迭代器基于快照,不支持remove,数据弱一致。

CopyOnWriteArrayList 适合什么场景
它只适合读多写少、且写操作不频繁的并发场景。比如监听器列表、配置项缓存、状态快照等——读操作可以无锁高速执行,但每次 add、remove 都会复制整个数组,写越频繁,内存和 CPU 开销越大。
- 写操作触发全量数组拷贝,10 万元素的列表
add一次可能分配数 MB 临时对象 - 迭代器不支持
remove(调用会抛UnsupportedOperationException) - 修改对正在迭代的线程不可见:迭代器持有的是快照,哪怕其他线程刚改完,你也看不到
典型误用:把它当普通 ArrayList 替代品塞进高频增删的队列或缓存更新逻辑里——性能会断崖式下跌。
为什么不能用它做实时状态同步
因为它的“一致性”是弱一致:写操作完成 ≠ 所有读线程立刻看到新值。每个读线程拿到的迭代器,都基于某次写操作完成时的数组副本,中间发生的多次修改全被忽略。
-
size()返回的是当前副本长度,但可能和最新写入后的真实逻辑大小不一致 -
contains()可能返回false,即使刚有线程add了该元素(还没轮到你这轮快照) - 没有阻塞或等待机制,无法实现“等写完再读”的语义
如果你需要“写完立刻可读”,别碰 CopyOnWriteArrayList,改用 ConcurrentHashMap 或加显式锁的 ArrayList。
立即学习“Java免费学习笔记(深入)”;
和 Vector / Collections.synchronizedList 对比
三者都能线程安全,但策略完全不同:
-
Vector是方法级synchronized,读写都串行,吞吐低,已基本淘汰 -
Collections.synchronizedList(new ArrayList())同样靠锁,但迭代必须手动同步块,否则仍可能ConcurrentModificationException -
CopyOnWriteArrayList读无锁、写独占,适合读压远大于写压的场景
关键区别在迭代:前两者迭代时若发生写,大概率崩;而 CopyOnWriteArrayList 迭代永远安全,代价是数据可能滞后。
常见报错和调试线索
遇到以下现象,大概率是误用了 CopyOnWriteArrayList:
- 日志里反复出现
OutOfMemoryError: Java heap space,堆 dump 显示大量Object[]实例,且生命周期短 - 单元测试里用
assertEquals(expected, list)偶发失败,实际打印发现 list 看似没变 - 调用
list.iterator().remove()直接抛UnsupportedOperationException
检查点:是否在循环中反复 add?是否依赖 contains 的实时性?是否把迭代器当成“活视图”来用?这些地方一踩一个准。
它的设计目标很窄:扛住成百上千个线程同时读,且写操作稀疏。超出这个边界,就不是“线程安全”的问题,而是“语义错配”的问题。









