copyonwrite容器适用于读多写少且弱一致性可接受的场景:事件监听器注册表、静态配置缓存、日志处理器列表、只读元数据索引;其读操作无锁,写操作加锁复制数组,迭代器基于快照,不支持迭代中修改。

在读多写少的并发场景中,CopyOnWrite 容器(CopyOnWriteArrayList 和 CopyOnWriteArraySet)通过“写时复制”(Copy-On-Write, COW)机制,让读操作完全无锁、线程安全且不抛 ConcurrentModificationException,而写操作虽加锁但仅影响极少数变更,整体兼顾了高吞吐与弱一致性保障。
适合哪些典型读多写少场景
这类容器不是通用线程安全替代品,而是为特定模式设计的:
- 事件监听器或回调注册表:如 GUI 事件分发、Spring
ApplicationListener、自定义发布-订阅系统——注册/注销极少,遍历通知极频繁 - 静态配置缓存:白名单、开关项、枚举状态集合等启动后基本不变、仅运维低频更新的数据
- 日志处理器列表:应用启动时加载若干
Handler,运行期几乎不增删,但每条日志都要遍历执行 - 只读元数据索引:例如服务发现中的健康实例快照列表,变更由定时同步驱动,非实时强一致
核心使用方式与关键细节
用法看似简单,但语义和行为与其他 List/Set 有本质差异:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
-
读操作零同步:调用
get()、size()、增强 for 循环、iterator()全部不加锁,直接访问当前数组快照;性能接近原生ArrayList -
写操作即复制:每次
add()、remove()、set()都会加ReentrantLock,复制整个底层数组,在副本上修改,再原子替换引用——这意味着写延迟不可忽略,且内存瞬时翻倍 -
迭代器是快照,不反映写变更:遍历时即使其他线程刚完成
add(),当前循环仍按旧数组执行,不会漏项也不会并发异常;但也意味着读不到“刚刚写入”的最新值 -
不支持迭代中修改:
iterator().remove()直接抛UnsupportedOperationException;也不建议在 for-each 中调用remove(),逻辑易错且无效
为什么不能滥用或误替其他集合
它解决的是“读高并发 + 写低频 + 弱一致性可接受”的三角约束,不是万能解:
- 不适合高频写:比如订单状态变更列表、用户在线会话 ID 动态集合——写操作串行化 + 数组复制会迅速拖垮吞吐,GC 压力陡增
- 不适合强一致性要求:写完后读取可能短暂看到旧值(窗口极短但存在),若业务依赖“写后立即可见”,应选
ConcurrentHashMap或加锁同步 - 不能替代
ArrayList或ConcurrentHashMap:前者无并发安全,后者是 Map 结构;若需并发有序可索引列表,COW 是少数可行选择之一 -
CopyOnWriteArraySet底层基于CopyOnWriteArrayList实现,增删查都走线性扫描,大数据量下contains()是 O(n),注意规模边界
一个轻量级监听器管理示例
体现其最自然的用法模式:
// 线程安全的监听器容器,无需额外同步
private final CopyOnWriteArrayList
public void register(DataChangeListener l) {
listeners.add(l); // 写:加锁 + 复制数组
}
public void notifyChange(Data data) {
// 读:无锁遍历快照,即使 register/remove 正在执行也不受影响
for (DataChangeListener l : listeners) {
l.onChange(data);
}
}
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










