fail-fast 仅是调试辅助机制,不保证捕获所有并发修改:因 modCount 非 volatile、比对无同步保护、结构性修改才触发校验,且官方明确其不可用于运行时逻辑控制。

因为 fail-fast 不保证一定能捕获所有并发修改,它只在“恰好被检查到”的时刻抛异常,而不是靠锁或同步来强制保障一致性。
modCount 检查不是原子操作
fail-fast 依赖 modCount 和 expectedModCount 的比对,这个比对发生在迭代器的 next() 或 remove() 调用时。但两次读取 modCount(一次在创建迭代器,一次在 checkForComodification)之间没有同步保护。如果另一个线程在比对前刚改完集合、又迅速恢复原状(比如 remove 后立即 add 回去),或者修改发生在检查间隙,异常就可能漏掉。
单线程下也会漏检
即使没有多线程竞争,只要结构性修改没触发校验点,就不会报错。例如:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 在 for-each 循环末尾修改集合(最后一次 next() 已执行完,不再校验)
- 用 list.set(i, x) 替换元素——这不是结构性修改,不改变 modCount,完全绕过 fail-fast
- 迭代器自己调用
it.remove()——它会同步更新 expectedModCount,不会触发异常
没有内存屏障和 happens-before 保证
modCount 是普通字段,不是 volatile。JVM 可能将其缓存在线程本地,导致一个线程看不到另一个线程对 modCount 的更新。即使实际发生了修改,检查时读到的仍是旧值,从而跳过异常抛出。
立即学习“Java免费学习笔记(深入)”;
设计定位就是调试辅助,不是运行时防线
Java 官方文档明确说明:ConcurrentModificationException 仅用于发现 bug,不能作为程序逻辑分支依据。它不替代同步机制(如 synchronized)、也不替代线程安全集合(如 CopyOnWriteArrayList)。真正需要并发修改的场景,应主动选用 java.util.concurrent 包下的类,而不是指望 fail-fast 来兜底。

















