Java中Iterator本身不支持多线程安全遍历,关键在于选用线程安全集合(如CopyOnWriteArrayList、ConcurrentHashMap)及匹配的遍历策略,避免遍历中修改原集合,必要时加锁或改用并行流。

Java 中 Iterator 本身不支持多线程安全遍历——它默认是 fail-fast 的,只要集合被其他线程修改(哪怕只是 add/remove),当前迭代器就会立即抛 ConcurrentModificationException。所以“安全遍历多线程集合”的关键,不是改造 Iterator,而是选用**线程安全的集合 + 匹配的遍历策略**。
选对集合:用 Fail-Safe 集合替代普通集合
普通 ArrayList、HashMap 的迭代器会检查内部修改计数(modCount),一变就崩;而某些并发集合在迭代时基于快照或读写分离,天然兼容多线程遍历:
- CopyOnWriteArrayList:迭代时读取的是创建时刻的数组副本,写操作(add/remove)会新建数组并替换引用,不影响正在遍历的迭代器。适合读多写少场景。
- ConcurrentHashMap:keySet()、values()、entrySet() 返回的迭代器是弱一致性(weakly consistent),不抛 CME,可能反映部分更新,但不会阻塞或崩溃。
- CopyOnWriteArraySet:底层基于 CopyOnWriteArrayList,行为类似,适用于去重且读远多于写的 Set 场景。
避免在遍历中修改原集合
即使用了线程安全集合,也尽量不在迭代过程中调用其修改方法(如 list.add() 或 map.put()),除非明确知道该集合允许(例如 CopyOnWriteArrayList 允许,但 ConcurrentHashMap 的迭代器不支持 remove(),也不建议在遍历时 put):
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- CopyOnWriteArrayList 的 iterator.remove() 是支持的,但实际是移除快照里的元素——对原集合无影响(因为快照已固定);真正删除需靠写线程调用 list.remove()。
- ConcurrentHashMap 的迭代器不支持 remove(),调用会抛
UnsupportedOperationException;如需边遍历边删,应改用map.keySet().removeIf(...)或显式加锁。
必要时加同步或使用更高级工具
若必须用非并发集合(如 ArrayList)且需多线程协作遍历,不能只靠 Iterator 解决:
- 用
Collections.synchronizedList(new ArrayList())包装后,所有操作(包括 iterator())都需手动同步块包裹,否则 iterator() 返回的迭代器仍可能是非线程安全的。 - 更推荐用
ReentrantLock控制临界区,或改用 Stream 并行处理(list.parallelStream().filter(...).collect(...)),让框架管理线程安全逻辑。 - 若业务逻辑复杂(如遍历中触发远程调用再决定是否删),优先考虑先收集待处理项(如 ID 列表),遍历结束后统一操作,规避遍历期修改。
别踩坑:增强 for 循环不等于更安全
增强 for(for-each)只是 Iterator 的语法糖,底层仍走 fail-fast 逻辑。用它遍历非线程安全集合,在多线程下一样抛异常。它既不提升安全性,也不支持 remove 操作——想删只能显式获取 Iterator。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










