Java中并发修改异常如何处理
在 Java 中,ConcurrentModificationException
(并发修改异常) 是遍历集合时最常见的错误之一。它发生在迭代过程中直接修改集合结构(添加/删除元素)时,与是否多线程无关。以下是详细的处理方案:
一、异常触发场景
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));// 会抛出 ConcurrentModificationException
for (String s : list) {if ("B".equals(s)) {list.remove(s); // 直接调用集合的 remove()}
}
二、解决方案(单线程环境)
1. 使用 Iterator.remove()
(推荐)
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {String s = iterator.next();if ("B".equals(s)) {iterator.remove(); // 安全删除当前元素}
}
2. Java 8+ 使用 removeIf()
list.removeIf(s -> "B".equals(s)); // 一行代码解决
3. 使用 CopyOnWriteArrayList
(读多写少场景)
List<String> list = new CopyOnWriteArrayList<>(Arrays.asList("A", "B", "C"));
for (String s : list) {if ("B".equals(s)) {list.remove(s); // 安全但性能较低(每次修改复制整个数组)}
}
4. 遍历时记录待删元素,遍历后统一删除
List<String> toRemove = new ArrayList<>();
for (String s : list) {if ("B".equals(s)) {toRemove.add(s);}
}
list.removeAll(toRemove);
5. 使用下标遍历(仅适用于 ArrayList
等随机访问集合)
for (int i = 0; i < list.size(); i++) {String s = list.get(i);if ("B".equals(s)) {list.remove(i);i--; // 修正索引}
}
三、解决方案(多线程环境)
1. 使用并发集合类
// 线程安全的 List
List<String> list = Collections.synchronizedList(new ArrayList<>());// 遍历时需手动同步
synchronized (list) {Iterator<String> it = list.iterator();while (it.hasNext()) {String s = it.next();// 操作元素}
}
2. 使用 java.util.concurrent
包中的集合
// 高性能并发 List
List<String> list = new CopyOnWriteArrayList<>();// 并发 Map
Map<String, String> map = new ConcurrentHashMap<>();
3. 使用锁机制
List<String> list = new ArrayList<>();
ReentrantLock lock = new ReentrantLock();// 写操作加锁
lock.lock();
try {list.add("X");
} finally {lock.unlock();
}// 遍历时加锁
lock.lock();
try {for (String s : list) { /* ... */ }
} finally {lock.unlock();
}
四、关键预防原则
-
禁止在遍历中直接修改集合
使用iterator.remove()
是唯一安全的修改方式。 -
多线程环境优先选并发集合
ConcurrentHashMap
>Collections.synchronizedMap()
CopyOnWriteArrayList
>Collections.synchronizedList()
-
Java 8+ 优先使用 Stream API
list = list.stream().filter(s -> !"B".equals(s)).collect(Collectors.toList());
-
避免在迭代中调用会修改集合的方法
包括:add()
,remove()
,clear()
,addAll()
等。
五、常见误区
方案 | 问题 | 推荐替代 |
---|---|---|
Collections.synchronizedList() | 遍历时不同步仍会抛异常 | 遍历时加锁或用 CopyOnWriteArrayList |
用 for 循环删除元素 | ArrayList 删除后索引错位 | 用 Iterator.remove() 或倒序遍历 |
多线程用普通集合 + 同步块 | 易遗漏同步导致并发问题 | 直接使用 ConcurrentHashMap 等 |
最佳实践:单线程用
Iterator.remove()
或removeIf()
,多线程用并发集合类,Java 8+ 可配合 Stream API 实现无迭代修改。