expectedmodcount 仅存在于 arraylist.itr 和 listitr 中,用于 fail-fast 校验;arraylist.spliterator 不使用该机制,而是基于数组快照实现弱一致性遍历,不检查 modcount,也不抛 concurrentmodificationexception。

Java 中 ExpectedModCount 并不直接出现在 ArrayList.Spliterator 的源码里,它其实是 ArrayList.Itr(普通迭代器)和 ArrayList.ListItr 中用于快速失败(fail-fast)校验的字段。而 Spliterator 在并行遍历中**不使用 expectedModCount 机制**,它的并发安全模型完全不同。
ArrayList.Spliterator 不依赖 expectedModCount
ArrayList 的 spliterator() 方法返回的是一个内部静态类 ArrayListSpliterator,它继承自 java.util.Spliterator 接口,并没有维护或检查 modCount 或 expectedModCount 字段。它通过以下方式规避并发修改问题:
- 构造时捕获当前数组引用和长度(
array,hi,lo),后续遍历基于这个快照进行 - 所有操作(如
tryAdvance、forEachRemaining、trySplit)都只读取已捕获的数组,不检查结构是否被修改 - 即使外部在遍历过程中调用
add、remove,只要不触发数组扩容(即未改变当前快照所引用的数组对象),遍历仍能继续——但结果可能不反映最新状态(“弱一致性”)
为什么 Spliterator 不做 modCount 校验?
因为 Spliterator 的设计目标是支持**可分割、可并行、弱一致性**的遍历,而非严格实时一致性。它有意放弃 fail-fast 语义,以换取更高的并发吞吐量和更灵活的并行执行能力:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 并发修改检测会引入额外同步开销,违背并行遍历的性能初衷
- 多个线程同时遍历同一
Spliterator分割出的子任务时,无法也不应相互阻塞或校验全局modCount - 规范明确要求
Spliterator是“weakly consistent”,允许遍历时看到部分更新、遗漏或重复元素,但不抛ConcurrentModificationException
对比:普通 Iterator 为何需要 expectedModCount?
普通迭代器(Itr)是单线程、顺序、强一致性的抽象:
- 创建时记录当前
modCount到expectedModCount - 每次调用
next()或hasNext()前,都会检查modCount == expectedModCount - 一旦发现不等(说明集合被结构性修改),立即抛出
ConcurrentModificationException - 这种机制对单线程误改(如边遍历边
remove)非常有用,但完全不适用于并行场景
实际使用中的注意事项
使用 ArrayList.spliterator() 进行并行流操作时,需清楚其行为边界:
- 不保证实时性:遍历开始后对列表的增删操作,不会中断遍历,但新元素可能不可见,已删除元素可能仍被访问
-
扩容是关键分界点:如果遍历中触发了
ensureCapacity导致数组替换(新数组对象),旧Spliterator仍持有原数组引用,不会感知该变化,也不会报错 -
线程安全需自行保障:若多个线程既读又写同一
ArrayList,仍需外部同步(如Collections.synchronizedList或改用CopyOnWriteArrayList)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










