copyonwritearraylist遍历时不会抛出concurrentmodificationexception,因其迭代器创建时持有数组副本快照,后续遍历基于该静态快照,写操作替换新数组不影响旧快照。

CopyOnWriteArrayList 通过写时复制机制,让遍历操作永远基于一个不可变快照,从而天然避免 ConcurrentModificationException,无需额外加锁。
为什么遍历时不会抛出 ConcurrentModificationException
它的迭代器(Iterator)在创建时会持有当前数组的副本引用,后续所有遍历都基于这个静态快照。即使其他线程同时 add、remove,原数组被替换为新副本,也不会影响正在遍历的旧副本。
注意:这不等于“实时一致性”——遍历看到的是某次写操作前的数据状态,适合读多写少、允许短暂数据滞后的场景(比如监听器列表、配置缓存)。
写操作会触发数组复制,代价较高
每次 add、set、remove 都会:
- 加锁(ReentrantLock)确保写操作互斥
- 复制整个底层数组(Arrays.copyOf)
- 在新数组上完成修改
- 用新数组原子替换旧数组引用
因此,频繁写入(如每秒数百次以上)会导致大量内存分配和 GC 压力,应避免用于高并发写场景。
遍历过程不能修改集合本身
迭代器的 remove()、add() 方法直接抛 UnsupportedOperationException —— 这是设计使然,防止破坏快照语义。若需边遍历边删,可先收集待删元素,遍历结束后统一调用 removeAll()。
例如:
Listfor (String s : list) {
if (s.startsWith("temp")) toRemove.add(s);
}
list.removeAll(toRemove);
替代方案需按场景权衡
如果写操作较多但又需线程安全遍历,可考虑:
- 使用 Collections.synchronizedList + 手动同步遍历块(需显式 synchronized(list))
- 读多写少且要求强一致性时,用读写锁(ReentrantReadWriteLock)保护普通 ArrayList
- 纯遍历+少量写,仍首选 CopyOnWriteArrayList;纯高频写,改用 ConcurrentHashMap 或并发队列
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











