Java集合迭代失效机制本质是Fail-Fast逻辑一致性保护,通过modCount与expectedModCount校验实现;非线程安全机制,单线程误用remove()亦触发;需依业务选Fail-Safe或分片等策略。

Java 集合迭代失效机制的核心不是“出错”,而是设计意图明确的主动拦截——它用 modCount 与 expectedModCount 的比对,在迭代器每次推进前确认集合结构是否被意外改动。理解这一点,是建立多线程迭代正确认知的第一步。
认清 Fail-Fast 不是线程安全机制
ConcurrentModificationException 看似是多线程问题,实则单线程下也会触发:只要遍历中调用 list.remove() 而非 iterator.remove(),就会因 modCount 变而 expectedModCount 不变导致校验失败。这说明:
- Fail-Fast 是逻辑一致性保护,不是并发控制方案
- Vector 或
Collections.synchronizedList()无法消除该异常,因为它们只同步单个方法,不保证跨 next() 调用的迭代状态一致性 - 加 synchronized 包裹整个 for-each 循环,只是把并发变成串行,没解决根本问题
区分两类迭代器行为本质
Java 中实际存在两种迭代语义:
- Fail-Fast 迭代器(ArrayList、HashMap、HashSet 等):依赖实时 modCount 校验,要求“遍历过程零修改”,共享即危险
- Fail-Safe 迭代器(CopyOnWriteArrayList、ConcurrentHashMap.keySet().iterator()):基于快照或弱一致性视图,允许遍历时其他线程写入,但不保证看到最新变更
关键不在“能不能并发”,而在“你接受什么一致性模型”:强一致(需锁)、最终一致(可容忍短暂延迟)、还是读隔离(只看快照)?选型必须匹配业务语义。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
掌握四种实用且互斥的多线程遍历策略
没有银弹,只有适配场景的选择:
-
独立迭代器 + 不可变/低频写数据源:每个线程调用
list.iterator()自建游标,适用于静态配置、启动后只读的列表 - CopyOnWriteArrayList 快照遍历:读操作无锁、不抛异常,适合监听器注册表、白名单等读远多于写的场景;写操作开销大,禁用于高频增删
-
预分片 + 子列表副本:主线程用
Lists.partition()或手动计算索引区间切分,每个线程处理一份new ArrayList(original.subList(i, j)),彻底隔离状态 -
parallelStream() 替代显式迭代:底层自动分片+ForkJoin,规避游标管理;注意避免副作用,如需更新共享变量,应使用
AtomicInteger、LongAdder或收集到局部再合并
避开三个典型认知误区
这些做法看似“加了锁”或“用了线程安全集合”,实则埋下隐患:
- 把同一个 Iterator 实例传给多个线程 —— 游标字段(如 cursor、lastRet)被交叉覆盖,结果不可预测
- 认为
synchronized(list)后 for-each 就安全 —— 若 list 是 synchronizedList 包装,内部锁对象可能不是 list 本身,易失效;且仍无法防止其他线程通过不同引用修改 - 在 CopyOnWriteArrayList 迭代器上调用
remove()—— 它的迭代器不支持删除,会直接抛UnsupportedOperationException

















