arraylist的iterator在多线程下触发fail-fast机制,抛出concurrentmodificationexception;解决方式包括加锁同步、使用copyonwritearraylist、先复制再遍历或改用并发容器/流式处理。

ArrayList 的 Iterator 在多线程环境下直接使用会触发 fail-fast(快速失败),因为它内部维护了一个 modCount 计数器,一旦检测到集合被并发修改(非 Iterator 自身的 remove()),就会抛出 ConcurrentModificationException。这不是线程安全机制,而是一种调试保护。要安全地在多线程中遍历 ArrayList 并避免该异常,核心思路是:**避免结构修改与迭代同时发生,或改用线程安全的替代方案**。
加锁同步遍历
对整个遍历过程加锁,确保同一时间只有一个线程能访问并修改列表。适用于读多写少、且可接受短时阻塞的场景。
- 使用
synchronized块包裹迭代逻辑,锁对象建议是 ArrayList 本身(或其外部锁) - 若需在遍历时删除元素,必须用
Iterator.remove(),不能调用list.remove() - 注意:锁粒度较粗,可能影响吞吐量;写操作也会被阻塞
使用 CopyOnWriteArrayList
这是 JDK 提供的线程安全 List 实现,底层在每次修改时复制整个数组。它的 Iterator 是弱一致性的(不抛 ConcurrentModificationException),且遍历时不影响其他线程写入。
- 适合读远多于写的场景(如监听器列表、配置缓存)
- 遍历时即使其他线程 add/remove,Iterator 仍基于快照工作,不会失效
- 缺点:写操作开销大(复制数组)、内存占用高、无法保证实时性
先复制再遍历(客户端加锁 + 快照)
在进入遍历前,获取当前列表的不可变副本(如 new ArrayList(original)),然后在副本上安全迭代。写操作仍需同步控制。
- 读操作完全无锁,提升并发读性能
- 需保证复制时的线程安全:复制动作本身应受锁保护,防止复制中途被修改
- 适用于遍历频繁、修改较少,且能容忍短暂数据陈旧的业务
改用并发容器或流式处理
若业务允许结构调整,可考虑更合适的并发集合或函数式方式:
-
ConcurrentHashMap.values()转为 List 后遍历(适合键值映射场景) - 用
Stream配合parallelStream()处理只读逻辑(注意副作用和线程安全) - 对特定索引范围做分段处理(如按 size/n 切片,配合 ExecutorService 并行遍历)
fail-fast 不是 bug,而是提醒你存在未受控的并发修改。真正解决它,不是绕过异常,而是明确读写契约——要么串行化访问,要么切换到支持并发的语义模型。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











