arraylist的fast-fail机制仅检测并发结构性修改并立即抛出concurrentmodificationexception,不提供线程安全保护;其核心是volatile修饰的modcount字段,记录结构性修改次数,迭代器通过比对modcount与expectedmodcount来触发异常。

ArrayList 的 fast-fail 机制并不提供线程安全保护,它只是在**检测到并发结构性修改时立即报错**,而不是阻止或同步这些修改。modCount 是这个检测机制的核心“记账员”,它的作用不是防止并发,而是让问题暴露得更快、更明确。
modCount 是什么?
modCount 是定义在 AbstractList 中的 volatile int 字段,被 ArrayList 继承使用。它只记录集合的结构性修改次数(如 add、remove、clear、trimToSize 等改变元素个数或数组结构的操作),不记录 get、set 等读取或替换操作。
- 每次调用 add()、remove() 等方法,modCount 就 +1
- 初始值为 0,未修改时保持不变
- 它本身不加锁、不原子、不保证可见性顺序——但 volatile 保证了其他线程能及时看到变化(对 fail-fast 检测已足够)
多线程下如何触发检测?
当一个线程通过 iterator 遍历 ArrayList 时,迭代器在创建瞬间会把当前 modCount 的值拷贝到自己的 expectedModCount 字段中。之后每次调用 next() 或 remove(),都会执行 checkForComodification():
- 若 modCount == expectedModCount → 正常继续
- 若 modCount != expectedModCount → 立即抛出 ConcurrentModificationException
这个不等式在多线程场景下极易成立:比如线程 A 正在遍历,线程 B 同时调用 list.remove() → B 使 modCount 加 1,A 下一次调用 next() 时发现预期值不匹配,立刻失败。
为什么说它“不保护”,只是“报错”?
modCount 机制没有互斥控制、没有内存屏障保障完整一致性、也不阻塞任何线程。它只是在迭代路径上做一次快照比对:
- 无法防止两个线程同时修改 list(比如都调 remove)导致数据错乱
- 无法避免写写冲突、丢失更新、size 不一致等真实并发问题
- 异常抛出时机不确定(可能在第二次 next,也可能在 remove 调用后),属于事后检测
换句话说:modCount 不是锁,也不是同步工具;它是“哨兵”,职责是喊一声“有人偷偷改了!”,而不是拦住那人。
真正需要线程安全怎么办?
如果业务要求多线程安全读写,不能依赖 fail-fast 来“保护”,而应主动选用线程安全方案:
- 用 Collections.synchronizedList(new ArrayList()) 包装(注意:迭代仍需手动同步)
- 用 CopyOnWriteArrayList(适合读多写少,迭代时不抛 CME,但写操作开销大)
- 用显式锁(ReentrantLock)或并发容器(如 ConcurrentLinkedQueue 替代部分场景)
- 避免共享可变状态:将 ArrayList 设为局部变量、不可变副本,或用 Stream.collect 等无副作用方式处理
fail-fast 的价值,在于帮你尽早发现“本不该发生的并发访问”,而不是代替你解决并发问题。











