fail-fast靠modcount校验预警,不一致即抛concurrentmodificationexception;fail-safe如copyonwritearraylist基于不可变快照隔离读写,不校验、不抛异常;concurrenthashmap属弱一致性,非严格fail-safe。

Java 中 fail-fast 和 fail-safe 是两种应对“遍历时集合被修改”的不同策略,本质区别不在“是否抛异常”,而在于“如何应对修改”。
fail-fast:靠校验来预警
它不阻止修改,也不同步数据,而是用 modCount(修改计数器) 做运行时哨兵:
- ArrayList、HashMap 等集合每次 add/remove 结构性操作都会递增 modCount
- 迭代器创建时,把当前 modCount 复制为 expectedModCount
- 每次调用 next() 或 remove() 前,检查两者是否一致;不一致就立即抛 ConcurrentModificationException
- 这个机制不是锁,也不保证 100% 捕获并发修改——比如多线程竞态下可能漏检
- 单线程也会触发:for-each 遍历中调 list.remove() 就会失败,但 iterator.remove() 是合法的,会同步更新 expectedModCount
fail-safe:靠副本隔离读写
它不校验、不阻塞、不抛异常,而是让迭代器操作一个独立快照:
- CopyOnWriteArrayList 是典型:迭代器创建时复制当前数组引用(浅拷贝),后续遍历都在该副本上进行
- 原集合增删改 → 新建数组、更新引用,不影响已有迭代器
- 迭代器永远看不到新修改,只看到“创建那一刻”的状态
- 内存开销大(写操作要复制整个数组),读操作无锁、线程安全
ConcurrentHashMap 是个特例
它既不是 fail-fast,也不是严格意义上的 fail-safe:
- 迭代器不读 modCount,也不复制整个哈希表
- 按桶(bin)逐步访问,允许看到部分更新、部分未更新的数据
- JavaDoc 明确称其为 weakly consistent(弱一致性),不承诺快照,也不抛异常
- 真正符合 fail-safe 定义的,只有 CopyOnWriteArrayList 这类基于不可变快照的实现
怎么选?看场景
如果读多写少、要求遍历绝对不中断,且能接受旧数据和内存开销,用 CopyOnWriteArrayList;
如果需要实时性、结构一致性优先,或写操作频繁,用 ArrayList/HashMap + 合理同步(如 Collections.synchronizedList 或显式锁);
如果高并发读写都要兼顾,且能接受弱一致性语义,ConcurrentHashMap 更合适。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











