ArrayList迭代非线程安全,因fail-fast机制抛ConcurrentModificationException;可用CopyOnWriteArrayList(读多写少)、Collections.synchronizedList加手动同步、遍历前复制快照或正确使用Iterator/倒序/批量删除规避。

ArrayList 本身不支持多线程安全迭代,核心问题在于它的 Iterator 是 fail-fast 的:内部用 modCount 记录修改次数,一旦遍历中被其他线程(或本线程非 Iterator 方式)修改结构,就立即抛 ConcurrentModificationException。这不是偶然报错,而是设计上的主动拦截。解决的关键是**不让“边读边改”同时发生,或者换一个不依赖 modCount 检查的迭代方式**。
用 CopyOnWriteArrayList 替代(读多写少首选)
这是最直接、侵入性最小的方案,特别适合监听器列表、配置缓存等场景:
- 它的
Iterator基于创建时刻的数组快照,遍历时完全不加锁,也不会检查modCount,因此绝不会抛CME - 写操作(
add/remove)会复制整个底层数组,所以写性能差、内存占用高 - 遍历时看不到其他线程刚写入的新元素——这是弱一致性,但多数只读场景可接受
- 无需额外同步,代码干净:
List<string> list = new CopyOnWriteArrayList();</string>后可直接for (String s : list)
用 Collections.synchronizedList + 手动同步迭代
如果你已用 ArrayList 且不能轻易替换,又需要强一致性(要求遍历时看到最新数据),可用此方式:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
List<string> syncList = Collections.synchronizedList(new ArrayList());</string>只保证单个方法(如add()、get())线程安全 - 但
iterator()和遍历过程是复合操作,必须显式加锁:不加锁仍会 CME - 正确写法:
synchronized (syncList) { for (String s : syncList) { ... } } - 缺点:锁粒度大,读写都串行,高并发下吞吐受限;写操作也会阻塞读
先复制再遍历(客户端快照)
适用于读远多于写、且能容忍短暂延迟的场景,比 CopyOnWriteArrayList 更轻量:
立即学习“Java免费学习笔记(深入)”;
- 在遍历前,用
new ArrayList(originalList)创建一个当前状态的副本 - 后续所有遍历都在副本上进行,完全不受原列表并发修改影响
- 原列表的写操作仍需同步控制(比如用
synchronized包裹add/remove) - 内存开销比
CopyOnWriteArrayList小(只在遍历时复制一次),但频繁遍历仍可能有压力
避免在迭代中修改原列表(单线程也适用)
即使在多线程中,若确定某次遍历仅由一个线程执行(其他线程此时不写),也要注意删除方式:
- ❌ 错误:
for (String s : list) { if (s.isEmpty()) list.remove(s); }—— 触发 CME - ✅ 正确一:
Iterator<string> it = list.iterator(); while(it.hasNext()) { if (it.next().isEmpty()) it.remove(); }</string> - ✅ 正确二:倒序索引删:
for (int i = list.size()-1; i >= 0; i--) { if (list.get(i).isEmpty()) list.remove(i); } - ✅ 正确三:收集后批量删:
list.removeAll(toRemove),该方法不走迭代器路径

















