Java Iterator非线程安全,多线程遍历时易因并发修改导致ConcurrentModificationException或数据漂移;其fail-fast机制仅检测结构性修改,对set值等非结构性修改无感知;安全做法包括选用CopyOnWriteArrayList/ConcurrentHashMap、显式细粒度同步、避免先查后用、优先使用parallelStream,并加强代码审计。

Java Iterator 本身不是线程安全的,多线程遍历中若底层集合被并发修改,极易引发 ConcurrentModificationException 或更隐蔽的数据漂移——比如遍历途中元素被删、值被改、顺序错乱,导致业务逻辑读到过期、重复或丢失的数据。
为什么会出现数据漂移
Iterator 是“快照式”遍历:它不锁定集合,也不复制全部数据,而是依赖集合内部的 modCount(修改计数器)做快速失败(fail-fast)校验。但这个机制只对“结构性修改”(如 add/remove 元素)敏感,对“非结构性修改”(如 set 某个索引的值、Map 的 value 更新)完全无感知。这就造成:
- 遍历时其他线程调用
list.set(i, newValue),Iterator 仍返回旧值,但不会报错; - 先用
iterator.hasNext()判断有元素,再iterator.next()取值,中间被删掉——可能抛异常,也可能跳过、越界或返回 null; - 多个线程共用同一个 Iterator 实例(极不推荐),状态互相覆盖,next() 返回结果不可预测。
安全做法:选对容器 + 控制访问粒度
不靠“加锁 Iterator”,而靠“让底层容器支持并发 + 明确读写边界”:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 读多写少场景:用
CopyOnWriteArrayList或ConcurrentHashMap。它们的 Iterator 是弱一致性(weakly consistent)的——遍历时即使集合被修改,也不会抛异常,且能保证遍历的是某一时刻的稳定快照; - 写频繁+需强一致性:改用显式同步块,但锁范围要小,例如:
只锁住“取值-处理-更新”这一整段逻辑,而不是整个遍历循环; - 避免“先查后用”模式:比如
if (map.containsKey(k)) map.get(k),应直接用map.computeIfPresent(k, ...)或Optional.ofNullable(map.get(k))避免 TOCTOU(时序检查与时序使用)漏洞。
替代方案:用 Stream + 并行处理
如果目标是“并发处理集合中每个元素”,优先考虑 Collection.parallelStream():
立即学习“Java免费学习笔记(深入)”;
- 自动分片、线程池调度、无共享状态,天然规避 Iterator 状态竞争;
- 注意:parallelStream 不适合有外部副作用的操作(如修改共享变量、写文件),应确保操作是无状态的;
- 若需顺序保障或结果聚合,可用
collect(Collectors.toList())或自定义Collector。
审计与自查要点
在代码审查或安全扫描中,重点关注以下模式:
- for-each 循环内调用
collection.remove()或map.put(); - Iterator 遍历过程中,跨方法/跨线程修改同一集合实例;
- 使用普通
ArrayList/HashMap作为多线程间共享的“只读配置列表”,却未做不可变封装(如Collections.unmodifiableList()); - 日志或监控中出现
ConcurrentModificationException被静默吞掉,掩盖了真实竞态问题。

















