copyonwritearraylist通过写时复制实现线程安全:读操作无锁高性能,迭代器为快照;写操作加锁复制新数组并原子替换,适用于读多写少场景,不支持迭代中增删。

CopyOnWriteArrayList 通过“写时复制”机制解决并发修改安全问题,读操作完全无锁、写操作加锁并复制新数组,避免了 ConcurrentModificationException 和数据不一致。
读操作不加锁,性能高且线程安全
所有读方法(如 get()、size()、iterator())都不加锁,直接访问内部数组。因为写操作不会修改原数组,所以多个线程同时读不会互相干扰,也不会看到“半更新”状态。
注意:迭代器返回的是创建时数组的快照,后续其他线程的增删改对当前迭代器不可见——这是设计取舍,不是 bug。
写操作加锁 + 复制新数组
每次执行 add()、remove()、set() 等修改操作时:
- 先用 ReentrantLock 获取独占锁
- 拷贝当前数组到新数组(长度 ±1 或不变)
- 在新数组上完成修改
- 用新数组原子替换旧数组引用(volatile 写保证可见性)
这样,正在读的线程仍用旧数组,刚写的线程用新数组,天然隔离,无需同步读逻辑。
适用场景明确,不适合高频写
适合“读多写少”的场景,例如监听器列表、配置项缓存、白名单等:
- 读操作毫秒级响应,无竞争开销
- 单次写操作耗时与数组长度成正比(要复制),写越频繁、数组越大,性能下降越明显
- 内存占用临时翻倍(新旧数组共存一小段时间)
如果写操作频繁(比如每秒上百次),应考虑 ConcurrentHashMap 或分段加锁的 ArrayList 封装方案。
不支持在迭代过程中调用 remove() / add()
它的 Iterator 是只读快照,调用 iterator.remove() 会直接抛 UnsupportedOperationException。
若需边遍历边删除,正确做法是:
- 收集待删元素(如 ID 或索引)
- 遍历结束后统一调用 removeAll() 或逐个 remove()
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











