返回

Java 循环陷阱:在 for-each 中删除集合数据导致异常原理

后端

并发修改异常:理解和避免

并发修改异常(ConcurrentModificationException)是 Java 中的运行时异常,当我们在遍历集合(例如 ArrayList)时,如果在遍历过程中尝试修改集合(例如添加或删除元素),就会遇到此异常。

当多线程同时访问集合时,就会发生并发修改异常。如果一个线程正在遍历集合,而另一个线程同时修改集合,就会导致并发修改异常。

例如,以下代码演示了在遍历 ArrayList 时删除元素的常见场景:

List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");

for (String element : list) {
    if (element.equals("B")) {
        list.remove(element);
    }
}

当我们运行这段代码时,将会遇到并发修改异常。这是因为我们在遍历集合时尝试修改集合。

避免并发修改异常的技巧

要避免并发修改异常,我们可以采取以下技巧:

  1. 使用迭代器(Iterator)

使用迭代器(Iterator)来遍历集合,可以避免并发修改异常。迭代器提供了一种安全的方式来遍历集合,它允许我们在遍历过程中修改集合而不抛出异常。

List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String element = iterator.next();
    if (element.equals("B")) {
        iterator.remove();
    }
}
  1. 使用并发集合(Concurrent Collections)

Java 提供了一些并发集合,可以安全地用于多线程环境中。并发集合可以防止并发修改异常的发生。

例如,我们可以使用 ConcurrentHashMap 来代替 HashMap,使用 CopyOnWriteArrayList 来代替 ArrayList。

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("A", 1);
map.put("B", 2);
map.put("C", 3);

for (String key : map.keySet()) {
    if (key.equals("B")) {
        map.remove(key);
    }
}
  1. **使用 synchronized **

我们可以使用 synchronized 关键字来同步对集合的访问。这样可以防止多个线程同时修改集合,从而避免并发修改异常。

List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");

synchronized (list) {
    for (String element : list) {
        if (element.equals("B")) {
            list.remove(element);
        }
    }
}

结论

并发修改异常是一个常见的错误,它会导致程序崩溃。为了避免并发修改异常,我们可以使用迭代器、并发集合或 synchronized 关键字来同步对集合的访问。