快照机制通过定格创建时刻的数据状态实现遍历隔离,如copyonwritearraylist用数组引用而非复制数据,写操作新建数组替换引用,迭代器持旧引用保证读一致性,不抛concurrentmodificationexception且无需加锁,但写性能o(n)、内存翻倍、不支持remove。

快照机制的核心,是让迭代器在创建那一刻就“定格”住当前的数据状态,后续遍历完全基于这个静态视图,不受任何写操作干扰。
快照不是复制数据,而是捕获引用
以 CopyOnWriteArrayList 为例:调用 iterator() 时,并不拷贝数组内容,而是直接把当前底层数组的引用赋给迭代器内部字段。这个引用指向的是某一时刻的不可变数组实例。
- 写操作(如 add、remove)会新建数组、复制原内容、修改后替换引用,原数组毫发无损
- 正在运行的迭代器仍持有旧数组引用,所以遍历时看到的永远是创建时的数据
- 因此不会触发 ConcurrentModificationException,也不需要加锁读取
快照 ≠ 实时,也不保证“最新”
快照反映的是迭代器构造那一瞬间的状态,不是动态同步的视图。
- 如果构造后立刻有线程添加元素,这些新元素不会出现在本次迭代中
- 如果构造前已有元素被其他线程逻辑删除(如 ConcurrentLinkedQueue 中 item 设为 null),迭代器可能跳过它,也可能因链表结构未及时更新而短暂包含它
- Redisson 的 RList.iterator() 也是快照,但它是远程拉取再本地构建,一次网络请求就定格了那一刻的全量数据
快照机制依赖底层实现策略
不同集合的“快照”达成方式不同,不能一概而论为“复制一份”。
- CopyOnWriteArrayList:靠写时复制(COW)+ 数组引用快照,强一致性,读绝对隔离
- ConcurrentHashMap:不提供传统快照,其迭代器是弱一致性的——可能漏掉刚插入的、也可能包含已删除但尚未清理的条目
- ConcurrentLinkedQueue:不复制数据,只在构造时尽力找第一个非空节点作为起点,之后按 next 链推进,属于“尽力而为”的轻量快照
快照带来便利,也伴随代价
它解决了遍历中修改引发的异常问题,但设计上做了明确取舍。
- 读性能高、无锁,适合监听器列表、配置缓存等读多写少场景
- 写操作要复制整个数组,时间复杂度 O(n),高频写会严重拖慢性能
- 迭代器不支持 remove() 方法(调用直接抛 UnsupportedOperationException)
- 内存占用翻倍风险:旧数组在迭代完成前无法被回收,尤其大数据量时需谨慎
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











