Java.util.ConcurrentModificationException的理解与修改

本文详细解析了Java中ConcurrentModificationException异常的原因,并提供了两种有效的解决方案。通过具体示例展示了如何避免在迭代过程中修改集合而导致的并发修改异常。

在Java开发的过程中有没有遇到类似的异常信息 Exception in thread "main" java.util.ConcurrentModificationException,

下面介绍异常原因以及这种异常的改进方法,内容很简单,有什么问题还望指正。

假设我们要实现这样一个例子: 判断集合里面有没有"world"这个元素,如果有,就添加一个"javaee"元素

出现异常的代码如下:

import java.util.ArrayList;
import java.util.Iterator;

public class Test {

    public static void main(String[] args) {
        ArrayList<String> array = new ArrayList<String>();

        // 创建并添加元素
        array.add("hello");
        array.add("world");
        array.add("java");
        Iterator it = array.iterator();
        while (it.hasNext()) {
            String s = (String) it.next();
            if ("world".equals(s)) {
                array.add("javaee");
            }
        }
    }
}

1.异常解释

  • ConcurrentModificationException:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。
  • 产生的原因:
       迭代器是依赖于集合而存在的,在判断成功后,集合的中新添加了元素,而迭代器却不知道,所以就报错了,这个错叫并发修改异常。
       简单描述就是:迭代器遍历元素的时候,通过集合是不能修改元素的。
  • 如何解决呢?
       A:迭代器迭代元素,迭代器修改元素
       B:集合遍历元素,集合修改元素(普通for)

2.下面用两种方法去解决这个异常

import java.util.ArrayList;

public class Test {

    public static void main(String[] args) {
        ArrayList<String> array = new ArrayList<String>();

        // 创建并添加元素
        array.add("hello");
        array.add("world");
        array.add("java");
        // 方式1:迭代器迭代元素,迭代器修改元素
        // 而Iterator迭代器却没有添加功能,所以我们使用其子接口ListIterator
        // ListIterator lit = array.listIterator();
        // while (lit.hasNext()) {
        // String s = (String) lit.next();
        // if ("world".equals(s)) {
        // lit.add("javaee");
        // }
        // }
        // System.out.println("list1:" + array);
        // 方式2:集合遍历元素,集合修改元素(普通for)
         for (int x = 0; x < array.size(); x++) {
         String s = (String) array.get(x);
         if ("world".equals(s)) {
         array.add("javaee");
         }
         }

         System.out.println("list2:" + array);

        // 方式3:增强for循环
        // 增强for循环写的话会报同样的错误,因为它本身就是用来替代迭代器的
        // for (String s : array) {
        // if ("world".equals(s)) {
        // array.add("javaee");
        // }
        // }
        // System.out.println("list3:" + array);
    }
}
### Java `ConcurrentModificationException` 异常原因 在Java编程中,`ConcurrentModificationException` 是一种运行时异常,通常发生在尝试通过迭代器遍历集合对象的同时,该集合被其他方式修改的情况。具体来说,当使用迭代器(如 `Iterator` 或增强型for循环)遍历集合时,如果集合的结构发生了变化——即添加或删除元素,而这些操作不是由迭代器自身的移除方法完成的话,便会触发此异常[^1]。 这种机制是为了防止由于并发修改而导致的数据不一致问题。每当创建一个新的迭代器实例时,都会记录下当前集合的状态;之后每次调用迭代器的相关方法之前,都会检查这一状态是否改变过。一旦发现有未预期的变化发生,就立即抛出 `ConcurrentModificationException` 来阻止进一步的操作并提示开发者注意潜在的问题所在[^2]。 更确切地说,在实现上是通过维护两个计数器来达成这一点:一个是用来跟踪实际发生的结构性变更次数 (`modCount`) ,另一个则是期望看到的变更数量(`expectedModCount`) 。只有在这两者保持同步的前提下,才能继续安全地访问下一个元素;反之则意味着存在非法的并发修改行为,从而引发上述提到的异常情况[^3]。 另外值得注意的是,在某些特定场景下即使是在单线程环境中也可能遇到这个问题。比如在一个foreach循环体内间接改变了正在被枚举的对象列表本身的内容,这同样会被视为违反了迭代期间不允许随意更改的原则[^4]。 最后一点补充说明来自源码层面的理解:当我们调用了迭代器提供的 next() 函数获取下一个元素的时候,实际上会先执行一次名为 checkForComodification 的私有辅助函数来进行一致性校验工作。它简单地对比了一下 expectModCount 和 modCount 这两个成员变量之间的差异,如果不匹配就会立刻抛出 ConcurrentModificationException 实例告知外部出现了不应该有的改动动作[^5]。 ### 解决方案 针对以上描述的各种可能引起 `ConcurrentModificationException` 的情形,可以采取如下几种策略加以应对: #### 使用 CopyOnWriteArrayList 替代 ArrayList 对于那些读取频率远高于写入的应用场合而言,考虑采用 `CopyOnWriteArrayList` 类作为替代品不失为一个好的选择。因为它的设计特点决定了每一次更新操作都将导致整个底层数组副本重新构建,所以能够有效规避掉因共享资源竞争所带来的麻烦。 ```java import java.util.concurrent.CopyOnWriteArrayList; public class SafeListExample { public static void main(String[] args) { List<String> safeList = new CopyOnWriteArrayList<>(); // 安全地向列表中添加项目... safeList.add("Item"); for (String item : safeList){ System.out.println(item); } } } ``` #### 利用 Iterator 自带 remove 方法 当确实需要在遍历过程中动态调整容器大小时,应该优先选用显式的 iterator.remove() 调用来代替直接作用于目标 collection 上的任何增删指令。这样做不仅有助于维持程序逻辑上的清晰度,更重要的是能确保不会意外触犯到有关并发控制的规定限制条件。 ```java import java.util.*; public class SafeRemoveExample { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Iterator<Integer> it = numbers.iterator(); while(it.hasNext()){ Integer num = it.next(); if(num % 2 == 0){ it.remove(); // 正确做法 } } System.out.println(numbers); // 输出奇数值组成的序列 } } ``` #### 锁定区域保护关键代码段 为了更好地处理涉及多个线程共同参的任务流程,可以通过引入锁机制的方式强制使各参者按照既定顺序依次进入临界区执行敏感操作。这样一来便可以在一定程度上减少甚至消除不必要的冲突干扰现象的发生概率。 ```java synchronized(list){ Iterator<?> i = list.iterator(); // Must be in synchronized block while(i.hasNext()) foo(i.next()); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值