CopyOnWriteArrayList适合读多写少场景,因其读操作无锁、基于快照遍历不抛ConcurrentModificationException,而写操作需加锁复制数组,代价高故要求写频次远低于读频次。

在读多写少场景下,CopyOnWriteArrayList 是 Java 并发包中一个专为“高并发读、低频写”设计的线程安全列表。它通过写时复制(Copy-On-Write)机制,让读操作完全无锁,写操作加锁但只复制底层数组,从而避免读写冲突,特别适合配置项缓存、监听器列表、白名单等场景。
为什么 CopyOnWriteArrayList 适合读多写少
它的核心原理是:所有写操作(add、remove、set 等)都会先加锁,再创建原数组的全新副本,在副本上修改,最后用新数组原子替换旧引用;而读操作(get、iterator、size 等)全程不加锁,直接访问当前引用的数组。这意味着:
- 读操作零同步开销,吞吐量极高,且不会因写操作阻塞
- 迭代器基于快照,不会抛
ConcurrentModificationException,也不会反映写操作的实时变更 - 写操作代价较高(涉及数组复制和内存分配),所以必须确保写频次远低于读频次
典型使用方式与注意事项
声明和初始化与其他 List 类似,但需注意其语义差异:
- 用
new CopyOnWriteArrayList<>()创建,支持泛型和初始容量 -
get(int index)是 O(1) 安全读取,无需同步 - 遍历推荐用增强 for 循环或显式 iterator —— 它们返回的是快照,遍历时即使其他线程修改了列表,也不影响当前遍历结果
- 避免在循环中调用
remove(Object)或add(E)来修改正在遍历的实例,因为这不会影响当前迭代器,容易引发逻辑误解
一个实用示例:事件监听器管理
比如维护一组监听器,99% 时间在通知(读+遍历),极少增删监听器(写):
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
public class EventPublisher {
private final CopyOnWriteArrayList<EventListener> listeners = new CopyOnWriteArrayList<>();
public void addListener(EventListener l) {
listeners.add(l); // 写操作:加锁 + 复制数组
}
public void removeListener(EventListener l) {
listeners.remove(l);
}
public void notifyEvent(Event e) {
// 读操作:无锁遍历快照,安全高效
for (EventListener listener : listeners) {
listener.onEvent(e);
}
}
}
这里每次 notifyEvent 都能获得一致的监听器视图,即使有其他线程正在增删,也不会导致迭代异常或漏通知。
性能边界与替代建议
当写操作频率上升(如每秒数百次以上),CopyOnWriteArrayList 的数组复制开销会显著增加,GC 压力也会变大。此时可考虑:
- 写操作较频繁但读仍占优:改用
ConcurrentHashMap模拟 list 语义(如 key 为索引) - 需要强一致性读写:用
ReentrantReadWriteLock包裹普通ArrayList - 纯只读场景初始化后不再写:直接用
Collections.unmodifiableList包装
不复杂但容易忽略:它的弱一致性模型意味着写操作对读操作的可见性存在延迟(下一个读操作才能看到),业务逻辑不能依赖“写完立刻可读”。


















