CopyOnWriteArrayList通过写时复制实现线程安全,遍历时可安全修改:写操作新建数组并更新引用,迭代器基于创建时的快照工作,不抛ConcurrentModificationException,但不支持Iterator.remove()且存在弱一致性。

CopyOnWriteArrayList 是 Java 并发包(java.util.concurrent)中专为“读多写少”场景设计的线程安全列表。它在遍历时允许安全修改,核心原理是:**写操作(add、remove 等)会复制一份新数组,而迭代器始终基于创建时的快照工作,不受后续修改影响。**
为什么遍历时能安全修改?
普通 ArrayList 在迭代过程中调用 remove() 或 add() 会触发 ConcurrentModificationException;而 CopyOnWriteArrayList 的迭代器是“弱一致性”的——它不反映迭代开始后发生的写操作,也不抛异常。
关键点:
- 迭代器构造时,会保存当前数组的引用(即一个不可变快照);
- 所有写操作(如
add()、set()、remove())都会新建数组、复制原数据、再更新内部引用; - 旧迭代器仍指向原数组,因此不会出现并发冲突或结构变化异常。
遍历中添加/删除元素的正确写法
可以直接在 for-each 或显式迭代中调用 add() / remove(),无需额外同步:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(Arrays.asList("a", "b", "c"));
for (String s : list) {
if ("b".equals(s)) {
list.remove(s); // 安全!不会抛 ConcurrentModificationException
list.add("x"); // 安全!新元素只对后续迭代可见
}
}
注意:这次循环中新增的 "x" 不会被当前迭代访问到(因为迭代器基于旧快照),但下一次遍历时会包含它。
需要避免的误区
虽然线程安全,但仍有使用边界:
- 不适用于高频写场景:每次写都复制整个数组,时间与空间开销大;
-
迭代器不支持 remove() 方法:
Iterator.remove()会抛UnsupportedOperationException,必须调用列表自身的remove(); - 无法保证实时一致性:迭代器看到的是“过去某时刻”的视图,不适合需要强一致读写的逻辑;
-
size() 和遍历结果可能不一致:比如遍历中途有其他线程 add 了 10 个元素,
size()返回新值,但本次循环仍只遍历原始数量。
适用典型场景
适合监听器列表、配置项缓存、状态订阅者集合等:
- 注册/注销频繁但遍历更频繁(如事件通知);
- 写操作稀疏,且能容忍读取延迟(如 UI 组件监听器刷新);
- 多个线程只读 + 少量线程偶尔增删,无需阻塞读操作。
不复杂但容易忽略细节。用对场景,它能让并发遍历既简洁又可靠。

















