copyonwritearraylist不抛concurrentmodificationexception,因其迭代器基于创建时的不可变数组快照工作,写操作新建数组并更新引用,与迭代器完全隔离,实现读写分离和弱一致性。

ConcurrentModificationException在CopyOnWrite类中天然被规避,根本原因在于其设计机制:每次写操作(add、remove、set等)都创建新数组副本,而迭代器始终基于遍历开始时的快照工作,不与原集合共享状态,因此不会检测到“并发修改”。
CopyOnWriteArrayList如何做到不抛异常
它内部维护一个volatile数组引用。当调用iterator()时,返回的迭代器持有的是当前数组的**不可变快照**;后续所有写操作(如list.remove())会生成新数组并更新引用,但不影响已有迭代器——所以for-each循环中调用remove()不会触发异常,也不会影响本次遍历结果。
- 迭代过程完全无锁,读操作零成本
- 写操作代价高(复制整个数组),适合读远多于写的场景
- 注意:迭代器本身不支持add/remove,它的remove()方法会抛UnsupportedOperationException
CopyOnWriteArraySet同理适用
底层基于CopyOnWriteArrayList实现,同样提供弱一致性迭代:遍历时增删元素不影响当前迭代,也不会抛ConcurrentModificationException。
- 添加重复元素自动去重,行为与HashSet一致
- 遍历时调用set.remove()安全,但删除的是“下一个尚未遍历到的匹配项”,已进入迭代器的元素仍会被访问到
使用时的关键注意事项
CopyOnWrite容器不是万能解药,需结合场景谨慎选用:
- 写操作频繁时性能急剧下降,不适用于高写负载
- 迭代器看到的是“旧数据”,无法反映写操作的实时性(最终一致性)
- 不能替代同步逻辑:若业务要求读写强一致(如账户余额校验),仍需加锁或使用其他并发控制手段
- 仅解决ConcurrentModificationException,不解决业务逻辑竞态,比如两个线程同时remove同一元素,结果仍是成功一次、失败一次
对比普通ArrayList+同步块的差异
用synchronized包裹遍历和修改,虽可避免异常,但会阻塞所有读操作;而CopyOnWriteArrayList让读完全无阻塞,写独立进行——这是它真正的价值所在,而非单纯“不报错”。
- 同步方案:读写互斥,吞吐量低,适合小集合+强一致性要求
- CopyOnWrite方案:读写分离,读极致高效,适合大读小写+容忍短暂延迟的场景
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











