CopyOnWriteArrayList适用于读多写少且数据变动极少的场景,如监听器列表、配置白名单等;写操作因复制数组而较重,高频或大数据量时需谨慎使用。

在读多写少的并发场景中,CopyOnWriteArrayList 是一个简单有效的选择——它通过“写时复制”机制避免读操作加锁,让高并发读几乎零开销,但代价是写操作较重。关键在于用对场景,而不是盲目替换 ArrayList。
适合它的典型场景
它真正发挥价值的地方很明确:列表内容极少变动,但被大量线程频繁遍历或随机读取。比如:
- 系统中的监听器列表(Listener/Observer 注册后基本不删改)
- 配置项白名单、权限角色列表等只在初始化或运维时更新的数据
- 缓存中的热点 key 列表,读远多于 reload
如果写操作每秒发生几次以上,或者列表本身很大(如上万元素),每次 add/remove 都要复制整个数组,CPU 和 GC 压力会明显上升,这时就得考虑其他方案(如 ConcurrentHashMap + 分段读写,或读写锁控制的 ArrayList)。
用法上要注意的细节
它不是万能的线程安全“银弹”,几个常见误区得避开:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
迭代器不支持修改:调用
iterator().remove()会抛UnsupportedOperationException,因为迭代器基于快照,不能反向影响底层数组 -
写操作不立即对其他读可见?不准确:写操作完成后,后续所有新获取的迭代器或
get()都能看到最新值;但正在执行的迭代器仍看到旧快照——这是设计使然,不是 bug - size() 和 isEmpty() 是实时的:这两个方法返回的是当前最新数组的长度,不是快照里的,所以可以放心用于条件判断
和 ArrayList + 显式同步对比
如果你原来用 synchronized(list) 或 ReentrantLock 包裹 ArrayList,改成 CopyOnWriteArrayList 后:
- 读性能显著提升:不用竞争锁,多个线程可同时遍历互不影响
- 写吞吐下降:每次 add/remove 创建新数组 + 原子引用替换,比单纯加锁慢几倍到几十倍(取决于数组大小)
- 内存占用略高:旧数组在无引用前不会被回收,GC 压力稍增
所以升级前建议压测写路径,确认写频次和数据规模仍在可接受范围。
一个轻量替代思路(当 CopyOnWriteArrayList 过重时)
如果写操作虽少但数组较大,或者需要支持迭代中删除,可以考虑折中方案:
- 用
volatile List<T>引用 + 不可变列表(如Arrays.asList(...)或ImmutableList),每次更新都创建新列表并原子赋值 - 读操作直接访问 volatile 引用,天然线程安全且零锁;写操作由单线程或串行化完成
- 这样既避免复制大数组,又保留了读的无锁特性,只是更新逻辑需自行协调
本质上,这是手动实现了一层轻量级的“写时复制”,灵活性更高。


















