copyonwritearraylist 的迭代器是 fail-safe 的,基于数组引用快照实现:调用 iterator() 时保存当前数组引用,后续遍历只读该快照,不检查结构修改、不抛 concurrentmodificationexception;写操作新建数组并原子更新,不影响已有迭代器;迭代器只读,不支持增删改;适合读多写少场景。

CopyOnWriteArrayList 的 fail-safe 迭代器不校验结构变化,也不抛 ConcurrentModificationException,它靠“快照”工作——每次调用 iterator() 时,把当前底层数组的引用拷贝一份,后续所有 next()、hasNext() 都只在这个副本上读,和原列表完全隔离。
快照不是深拷贝,是数组引用快照
迭代器初始化时拿到的是当时 getArray() 返回的数组对象引用(不是新分配内存复制每个元素),这个引用被存进迭代器的 snapshot 字段。只要该数组没被 GC 回收,迭代器就能稳定遍历。
- 数组本身不可变:写操作(
add/remove)会新建数组、复制旧数据、再原子更新内部array字段,老数组不受影响 - 所以迭代器看到的永远是创建那一刻的状态,哪怕其他线程立刻增删了 10 个元素,它也看不到
- 元素对象若可变(如自定义对象),其内部字段仍可能被并发修改——快照只保证数组结构不变,不保证元素内容线程安全
遍历时修改列表完全不影响当前迭代
你在 for-each 循环里调 list.add(x) 或 list.remove(y),不会中断循环,也不会让迭代器跳过或重复元素。
- 新增元素只会出现在下一次
iterator()创建后的遍历中 - 删除元素只是让原列表下次迭代看不到它,但当前迭代器仍在老数组上走,该元素照常被访问到
- 监听器场景典型:某个监听器在
onEvent()中调removeListener(this),广播循环不受干扰,也不会漏掉或重复通知其他监听器
迭代器自己不能改,也不支持 add/remove/set
CopyOnWriteArrayList 的迭代器实现类 COWIterator 明确禁止修改操作:
- 调用
iterator.remove()会直接抛UnsupportedOperationException - 没有
add()或set()方法;它只读,设计目标就是提供稳定视图,不是用于边读边改 - 想安全删元素?单线程用
Iterator.remove()(注意是迭代器自己的 remove),多线程高频写则不适合用 CopyOnWriteArrayList
性能特点很鲜明:读快、写重、内存敏感
get 是纯数组访问,O(1),比普通 ArrayList 还略快(省了 modCount 检查);但每次写都要复制整个数组引用,代价随容量线性增长。
- 10 万个元素的列表,一次
add()就要拷贝 10 万次引用,GC 压力明显 - 多个线程同时写会竞争同一把
ReentrantLock,变成串行瓶颈 - 适合监听器列表、配置缓存这类读远多于写的场景;别把它当通用线程安全 List 替代品
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











