Java foreach ConcurrentModificationException解读

本文探讨了Java中使用BFS遍历二叉树时出现ConcurrentModificationException的原因及解决办法,通过具体代码示例解释了foreach循环背后的迭代器原理。

这里foreach指的是 for (int num: nums)这种用法

下面的代码

//BFS
List<Node> current = new ArrayList<>();
List<Node> next = new ArrayList<>();

while (!current.isEmpty()) {
   for (Node node : current) {
      next.add(node.left);  //ommit if not null
      next.add(node.right); //ommit if not null
   }
   current.clear();
   current = next;  //want to swap current and next, but mistake
}   

这段代码运行后会出现ConcurrentModificationException
问题出在将current指向next的object,
在foreach遍历current中,next做了修改,因为current也指向next,所以意味着current也在做修改
而foreach是基于iterator实现的,详细参照
一不小心就让Java开发者踩坑的fail-fast是个什么鬼?

抛出CME的地方是

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

可以看到当modCount != expectedModCount时抛出CME

而看ArrayList的底层代码发现,在add的时候modCount会+1
因此导致了上述问题

    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
### Java `foreach` 循环中的 `ConcurrentModificationException` #### 原因分析 当使用增强型for循环(`foreach`)遍历集合时,如果在迭代过程中修改了该集合(除了通过迭代器自身的移除操作),则会抛出 `ConcurrentModificationException`。此异常是为了防止并发修改带来的不确定性[^1]。 具体来说,在给定的例子中: ```java at java.util.ArrayList.forEach(ArrayList.java:1252) ``` 表明是在调用 `ArrayList` 的 `forEach` 方法期间发生的异常。这通常意味着在同一时间有另一个线程正在修改这个列表的内容,或者是同一个线程内存在不恰当的操作尝试改变被迭代对象的状态[^2]。 #### 解决方案 为了处理这种情况并避免 `ConcurrentModificationException`,可以采取以下几种方式之一来解决问题: ##### 使用 Iterator 进行安全删除 利用 `Iterator.remove()` 来代替直接对集合进行增删改查动作是最常见的做法。这种方式允许在一个元素被访问之后立即对其进行删除而不引发异常。 ```java List<String> list = new ArrayList<>(); // ... 添加一些字符串到list... Iterator<String> it = list.iterator(); while (it.hasNext()) { String item = it.next(); if (someCondition(item)) { // 如果满足某些条件,则执行删除 it.remove(); // 安全地移除当前项 } } ``` ##### 利用 CopyOnWriteArrayList 替代普通 List 实现类 对于多线程环境下的频繁读取场景,考虑采用 `CopyOnWriteArrayList` 类作为替代品。这类容器会在每次写入时创建一个新的副本,从而使得迭代过程不会受到其他地方更改的影响。 ```java import java.util.concurrent.CopyOnWriteArrayList; public class SafeIterationExample { public static void main(String[] args) { CopyOnWriteArrayList<Integer> numbers = new CopyOnWriteArrayList<>(Arrays.asList(1, 2, 3)); for(Integer num : numbers){ System.out.println(num); numbers.add(num * 2); // 不会引起 ConcurrentModificationException } System.out.println(numbers.toString()); } } ``` ##### 同步控制机制 确保同一时刻只有一个线程能够访问共享资源也是一种有效的策略。可以通过同步代码块或者锁机制实现这一点。 ```java synchronized(list) { for (String element : list) { // 对element做些事情 } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝羽飞鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值