发生ConcurrentModificationException异常的原因分析

用iterator遍历集合时要注意的地方:不可以对iterator相关的地方做添加或删除操作。下面用List为例来说明为什么会报ConcurrentModificationException 这个异常,其它集合类似可以自己思考。

public static void main(String[] args){

	List<String> set = new ArrayList<String>();
	set.add(“a10001″);
	set.add(“a10002″);
	set.add(“a10003″);
	set.add(“a10004″);
	set.add(“a10005″);
	set.add(“a10006″);
	set.add(“a10007″);

	List<String> del = new ArrayList<String>();
	del.add(“a10003″);
	del.add(“a10004″);
	del.add(“a10005″);

	for(String str : set)
	{
		if(del.contains(str))
		{
			set.remove(str);
		}
	}
}

Exception in thread “main” java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
at java.util.AbstractList$Itr.next(Unknown Source)
at com.debug.Debug.main(Debug.java:28)

大家都知道for(String str : set) 这句话实际上是用到了集合的iterator() 方法
在iterator的时候是产生了一个List的内部类Itr
JDK源码

public Iterator<E> iterator() {
	return new Itr();
}

在new Itr()时有一个关键性的操作

int expectedModCount = modCount;

再回头看一下List 的 remove方法

public boolean remove(Object o) {
	if (o == null) {
		for (int index = 0; index < size; index++)
			if (elementData[index] == null) {
				fastRemove(index);
				return true;
			}
	} else {
		for (int index = 0; index < size; index++)
			if (o.equals(elementData[index])) {
				fastRemove(index);
				return true;
			}
	}
	return false;
}

private void fastRemove(int index) {
	modCount++;
	int numMoved = size – index – 1;
	if (numMoved > 0)
		System.arraycopy(elementData, index+1, elementData, index, numMoved);
		elementData[--size] = null;
	}

再看一下 iterator.next()操作

public E next() {
	checkForComodification();
	try {
		E next = get(cursor);
		lastRet = cursor++;
		return next;
	} catch (IndexOutOfBoundsException e) {
		checkForComodification();
		throw new NoSuchElementException();
	}
}

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

相信看到这儿大家已经应该明白了为什么会出现在这个异常了。

总结:
iterator时将expectedModCount = modCount 在remove()时 modCount++ 在next()时

if (modCount != expectedModCount)
throw new ConcurrentModificationException();

一旦删除或添加元素后modCount,expectedModCount这两个值就会不一致,当next时就会报ConcurrentModificationException
了解了原由,解决方案就很简单了 在遍历时用一个集合存放要删除的对象 在遍历完后调用removeAll(Collection<?> c)就不会发生异常了。


转载自:http://www.strutshome.com/index.php/archives/569

在Java应用程序中,`java.util.ConcurrentModificationException` 是一个常见的运行时异常,通常出现在使用迭代器(如 `Iterator` 或 Java 8 的 `forEach`)遍历集合的过程中,当集合被并发修改时抛出该异常。结合日志中的信息: ``` org.apache.ibatis.exceptions.PersistenceException: ### Error updating database. Cause: java.util.ConcurrentModificationException ``` 表明在执行数据库更新操作时,底层某个集合结构发生了并发修改,导致迭代过程出现不一致状态。 ### 原因分析 1. **集合在遍历时被修改** 最常见的情况是在使用 `Iterator`、`for-each` 循环或 `Stream.forEach()` 遍历集合时,另一个线程或当前线程修改了集合的结构(如添加、删除元素),而未通过迭代器自身的 `remove()` 方法进行操作[^2]。 2. **多线程环境下的共享集合访问** 如果多个线程同时读写同一个集合,并且没有适当的同步机制(如 `Collections.synchronizedList()` 或使用 `ConcurrentHashMap` 等并发集合类),则极易引发此异常[^3]。 3. **MyBatis 参数设置阶段发生异常** 异常信息指出“`The error occurred while setting parameters`”,说明问题发生在 MyBatis 框架将参数映射到 SQL 语句的过程中。可能的原因是传入的参数对象内部包含了一个正在被遍历的集合,而该集合在映射过程中被修改[^1]。 4. **使用非线程安全的集合类处理嵌套结构** 在引用[4]中展示了涉及 `JSONObject` 和 `Set` 的复杂嵌套结构操作,在遍历过程中对集合进行修改或传递给流处理时,若未正确复制或同步集合内容,也可能触发此异常[^4]。 --- ### 解决方案与最佳实践 1. **避免在遍历时直接修改集合结构** 如果需要在遍历过程中删除元素,应使用迭代器的 `remove()` 方法: ```java Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String item = iterator.next(); if ("Two".equals(item)) { iterator.remove(); // 正确做法 } } ``` 2. **使用并发集合类** 在多线程环境中,应使用线程安全的集合实现,例如: - `CopyOnWriteArrayList`(适用于读多写少场景) - `ConcurrentHashMap` - 使用 `Collections.synchronizedList(new ArrayList<>())` 包装普通集合 3. **在遍历前创建集合副本** 如果逻辑允许,可以在遍历前复制一份集合用于遍历,防止原集合被修改影响迭代器状态: ```java Set<String> stringsCopy = new HashSet<>(originalSet); for (String s : stringsCopy) { // 安全操作 originalSet } ``` 4. **检查 MyBatis 映射逻辑中的参数结构** 如果异常发生在 MyBatis 执行 SQL 之前,应重点检查传入的参数对象是否包含动态变化的集合结构。建议在构建参数对象前完成所有集合修改操作,或使用不可变集合(如 `Collections.unmodifiableList()`)避免意外修改。 5. **使用 Stream API 时注意副作用** Java 8 的 `Stream.forEach()` 不适合用于有状态的操作,尤其不应在 `forEach` 中修改外部集合。可以考虑改用 `filter()`、`map()`、`collect()` 等无副作用方式处理数据。 --- ### 日志分析建议 - 查看完整的堆栈跟踪,定位抛出 `ConcurrentModificationException` 的具体代码位置。 - 检查是否在遍历过程中调用了集合的 `add()`、`remove()` 等方法。 - 分析是否在多线程环境下共享了非线程安全的集合。 - 若涉及 MyBatis,查看参数对象是否在构造过程中被并发修改或包含可变集合。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值