CopyOnWriteArrayList适合读多写少场景,因其读操作无锁、写操作复制数组并原子替换,保证高并发读性能,但写有复制开销且数据最终一致。

在读多写少的并发场景中,CopyOnWriteArrayList 是一个非常实用的线程安全集合类。它通过“读不加锁、写时复制”的策略,在高并发读操作下保持极佳性能,适合配置缓存、监听器列表、白名单等场景。
为什么 CopyOnWriteArrayList 适合读多写少?
它的核心机制是:所有写操作(add、remove、set)都会先复制整个底层数组,修改副本后再用原子引用替换原数组;而读操作(get、iterator、size)直接访问当前数组,完全无锁。这意味着:
- 读操作零同步开销,响应快、吞吐高
- 写操作有复制成本(时间 + 内存),且期间可能让旧读线程看到“过期”数据(最终一致性)
- 写操作之间互斥(内部用独占锁),但不影响读
典型使用方式与注意事项
声明和初始化与其他 List 类似,但要注意它不支持 null 元素(add(null) 会抛 NullPointerException):
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
常用操作示例:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
-
安全遍历:即使遍历时有其他线程写入,也不会抛
ConcurrentModificationException,迭代器基于创建时的快照 -
避免在循环中修改:比如边遍历边 remove —— 这不会影响当前迭代器,但逻辑上容易出错,应改用
removeIf()或收集待删元素后统一处理 -
批量写入慎用:频繁调用 add 多次会触发多次复制,可考虑先用普通 ArrayList 收集再一次性
addAll()
和 Collections.synchronizedList 的关键区别
两者都线程安全,但设计目标不同:
-
Collections.synchronizedList对所有方法加同一把锁,读写串行,高并发读时成为瓶颈 -
CopyOnWriteArrayList读操作无锁,写操作锁粒度细(仅写时阻塞其他写),更适合读远多于写的场景 - 若写操作占比超过 10%~20%,或集合很大(如数万元素),复制开销会明显上升,此时需评估是否换用
ConcurrentHashMap或分段锁结构
一个真实可用的小例子
比如维护一个实时更新的在线用户列表,查询频次极高,增删相对稀疏:
public class OnlineUserManager {
private final CopyOnWriteArrayList<String> users = new CopyOnWriteArrayList<>();
public void login(String userId) {
users.add(userId);
}
public void logout(String userId) {
users.remove(userId);
}
// 高频调用,无锁、快速返回
public List<String> getAllUsers() {
return new ArrayList<>(users); // 返回不可变副本更安全
}
public boolean contains(String userId) {
return users.contains(userId);
}
}
注意:getAllUsers() 返回新 ArrayList 而非原始引用,避免外部误操作影响内部状态。

















