遍历变量导致ConcurrentModificationException异常的问题

本文通过一个具体的示例探讨了ConcurrentModificationException异常的原因,并解释了在遍历集合时对其进行修改为何会导致该异常的发生。

 近期项目中遇到一个不是很常见的异常ConcurrentModificationException,排查问题后,发现是由于遍历某变量时,对变量进行修改导致的,在此记录一下。

先看测试代码

import java.util.ArrayList;


public class OtherMethod {
	private static ArrayList<String> myList = new ArrayList<String>();
	
	public static void main(String[] args) {
		OtherMethod.myList.add("1"); 
		OtherMethod.myList.add("2");
		for(String s:OtherMethod.myList){
			System.out.println(s);
			OtherMethod.myList.add("3");
		}
	}
}
运行后结果

1
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
	at java.util.ArrayList$Itr.next(ArrayList.java:831)
	at other.OtherMethod.main(OtherMethod.java:19)
可以看到,这时候程序已经出现错误信息。

 这里我使用的myList变量为类变量,根据jvm中内存分配信息我们知道,类变量在jvm中是分配在方法区(Method Area)中,而类的成员变量则会分配在堆(Heap)中,会不会是由于jvm本身的设计导致的这个异常呢?重新修改代码进行验证,代码如下:

import java.util.ArrayList;


public class OtherMethod {
	private ArrayList<String> myList = null;
	
	public static void main(String[] args) {
		OtherMethod method = new OtherMethod();
		method.myList.add("1"); 
		method.myList.add("2");
		for(String s:method.myList){
			System.out.println(s);
			method.myList.add("3");
		}
	}
	
	public OtherMethod(){
		myList = new ArrayList<String>();
	}
}
测试结果与第一次的一致,可以得出结论,该异常信息与变量类型是类变量还是成员变量无关。

 实际上,这个异常产生的原因是在迭代(遍历)Vector或者Map时,对内容进行修改。此时由于修改Vector或者Map时会改变大小,导致出现这种异常信息。所以,在修改List、ArrayList、HaseMap等变量时,一定要注意,不可在迭代(遍历)过程中,否则会出现错误信息。

如果需要详细了解改问题产生的原因,可参考以下文章。

集合迭代时对集合进行修改抛ConcurrentModificationException原因的深究以及解决方案



ConcurrentModificationException 是 Java 中的一种运行时异常(RuntimeException),属于 Java 集合框架(Java Collections Framework)中的一部分。该异常主要用于检测集合在遍历过程中是否被外部修改,从而避免不确定的遍历行为。 该异常的产生通常与集合的 fail-fast 机制有关。fail-fast 是 Java 集合框架的一种错误检测机制,用于在多线程或单线程环境下检测集合的并发修改。当一个线程在使用迭代器遍历集合时,如果另一个线程(或同一线程的其他部分)修改了集合的结构(如添加、删除或替换元素),迭代器会抛出 ConcurrentModificationException 异常以防止遍历结果的不一致性[^3]。 具体来说,ConcurrentModificationException 的触发条件包括: 1. **使用迭代器遍历集合时修改集合结构**:在使用 Iterator 或 ListIterator 遍历集合的过程中,如果直接通过集合对象(而非迭代器本身)对集合进行 add、remove 等结构性修改操作,则会触发此异常。 2. **多线程环境下的并发修改**:在多线程环境下,一个线程正在遍历集合的同时,另一个线程修改了集合的内容,这会触发 ConcurrentModificationException。 3. **频繁修改集合内容**:例如在 Android 开发中,RecycleView 的 Adapter 使用的 List 数据源如果在数据频繁更新(如频繁调用 add、remove 操作)时被迭代,也可能导致异常的发生[^4]。 该异常的抛出机制依赖于集合内部的 modCount 和 expectedModCount 两个变量。modCount 表示集合被结构性修改的次数,而 expectedModCount 是迭代器初始化时记录的 modCount 值。在迭代过程中,如果发现这两个值不一致,则说明集合被并发修改,从而抛出异常[^2]。 ### 避免和处理 ConcurrentModificationException 的策略 1. **使用迭代器自身的修改方法**:在遍历集合时,如果需要修改集合内容,应使用 Iterator 提供的 remove 方法,而不是直接调用集合的 remove 方法[^1]。 2. **使用线程安全的集合类**:在多线程环境中,可以使用 java.util.concurrent 包中的集合类(如 CopyOnWriteArrayList、ConcurrentHashMap 等),这些集合类在并发访问时不会抛出 ConcurrentModificationException。 3. **同步控制**:通过使用 synchronized 关键字或 Lock 对象对集合的访问进行同步,确保同一时间只有一个线程可以修改或遍历集合。 4. **避免在遍历时修改集合结构**:可以通过将需要修改的操作延迟到遍历完成后再执行,或者在遍历时创建集合的副本进行操作。 ```java // 使用迭代器的 remove 方法安全地删除元素 Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String item = iterator.next(); if (someCondition(item)) { iterator.remove(); // 正确的方式 } } ``` ### 示例代码:使用 CopyOnWriteArrayList 避免异常 ```java import java.util.concurrent.CopyOnWriteArrayList; public class ConcurrentExample { public static void main(String[] args) { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); list.add("A"); list.add("B"); list.add("C"); // 多线程遍历和修改不会抛出 ConcurrentModificationException new Thread(() -> { for (String s : list) { System.out.println(s); if (s.equals("B")) { list.remove("B"); } } }).start(); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值