在 java 中避免 concurrentmodificationexception 的核心做法是:不要在增强 for 循环或迭代器遍历过程中直接修改集合结构。对于单线程场景,使用 iterator.remove() 安全删除元素;对于多线程并发修改场景,改用 copyonwritearraylist 等线程安全集合,或在遍历时对原集合加锁。

单线程下如何安全地移除元素
当你在单线程环境中需要一边遍历列表一边移除特定元素时,直接使用增强 for 循环(foreach)会抛出异常。正确的做法是获取 Iterator 并调用其 remove() 方法,这样迭代器能同步更新内部状态。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class SafeRemoveExample {
public static void main(String[] args) {
List<string> list = new ArrayList();
list.add("A");
list.add("B");
list.add("C");
Iterator<string> it = list.iterator();
while (it.hasNext()) {
String item = it.next();
if (item.equals("B")) {
it.remove(); // 安全移除
}
}
System.out.println(list); // 输出: [A, C]
}
}</string></string>
上述代码中,list.remove() 会改变集合的 modCount,而迭代器期望的 expectedModCount 不会同步更新,从而触发检查失败。使用 it.remove() 则会自动同步这两个计数器。
多线程环境下如何避免冲突
在多线程场景中,如果一个线程正在遍历集合,另一个线程修改了该集合,也会引发此异常。此时应使用 CopyOnWriteArrayList,它在写入时复制整个数组,保证遍历使用的是副本,从而不会看到并发修改。
你是 TypeScript 项目架构专家,专注于脚手架搭建生产就绪的 Node.js 和前端应用。生成包含现代工具链(pnpm、Vite、Next.js)、类型
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.Iterator;
public class ConcurrentSafeExample {
public static void main(String[] args) {
CopyOnWriteArrayList<string> list = new CopyOnWriteArrayList();
list.add("X");
list.add("Y");
new Thread(() -> {
Iterator<string> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}).start();
new Thread(() -> {
list.add("Z"); // 安全添加,不影响遍历
}).start();
}
}</string></string>
这种方案牺牲了写性能以换取读操作的无锁化,适合读多写少的并发场景。若必须使用普通 ArrayList,则需在遍历时使用 synchronized 块锁定集合对象。
为什么 foreach 循环会抛出异常
Java 的增强 for 循环在编译后会转换为使用 Iterator 的形式。其底层逻辑是在每次调用 next() 时检查集合的修改次数是否与创建迭代器时的记录一致。如果中间调用了集合自身的 add 或 remove 方法,就会破坏这一契约。
import java.util.ArrayList;
import java.util.List;
public class ForeachFailExample {
public static void main(String[] args) {
List<string> list = new ArrayList();
list.add("Item1");
list.add("Item2");
try {
for (String item : list) {
list.remove(item); // 错误操作
}
} catch (Exception e) {
System.out.println(e.getClass().getSimpleName());
}
}
}</string>
运行结果将打印 ConcurrentModificationException。这是因为 for-each 语法糖掩盖了底层的迭代器机制,开发者容易误以为可以直接操作集合。理解这一转换过程有助于在编码时主动规避此类陷阱。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










