ArrayList 并发操作 ConcurrentModificationException 异常

本文深入探讨了在Java中ArrayList的并发修改导致的ConcurrentModificationException异常,分析了其内部实现原理,包括modCount和expectedModCount的作用。并提供了两种解决方案,一是使用迭代器的remove方法,二是采用CopyOnWriteArrayList并发集合类。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、故障现象

ArrayList在迭代的时候如果同时对其进行修改就会抛出java.util.ConcurrentModificationException异常

2、故障代码

public class ArrayListTest {

    public static void main(String[] args) {

        List<String> lists = new ArrayList<>();
        lists.add("a");
        lists.add("b");

        Iterator<String> iterator = lists.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            if (next == "b") {
                lists.remove(next);
            }
        }
    }
}

异常截图

ConcurrenModificationException异常

3、导致原因

通过查看异常,发现异常出现的位置在 java.util.ArrayList类的内部类Itr中的checkForComodification方法中

/**
 * An optimized version of AbstractList.Itr
 */
private class Itr implements Iterator<E> {
	int cursor;       // index of next element to return
	int lastRet = -1; // index of last element returned; -1 if no such
	int expectedModCount = modCount;

	Itr() {}

	public boolean hasNext() {
		return cursor != size;
	}

	@SuppressWarnings("unchecked")
	public E next() {
		checkForComodification();
		int i = cursor;
		if (i >= size)
			throw new NoSuchElementException();
		Object[] elementData = ArrayList.this.elementData;
		if (i >= elementData.length)
			throw new ConcurrentModificationException();
		cursor = i + 1;
		return (E) elementData[lastRet = i];
	}

	public void remove() {
		if (lastRet < 0)
			throw new IllegalStateException();
		checkForComodification();

		try {
			ArrayList.this.remove(lastRet);
			cursor = lastRet;
			lastRet = -1;
			expectedModCount = modCount;
		} catch (IndexOutOfBoundsException ex) {
			throw new ConcurrentModificationException();
		}
	}

	@Override
	@SuppressWarnings("unchecked")
	public void forEachRemaining(Consumer<? super E> consumer) {
		Objects.requireNonNull(consumer);
		final int size = ArrayList.this.size;
		int i = cursor;
		if (i >= size) {
			return;
		}
		final Object[] elementData = ArrayList.this.elementData;
		if (i >= elementData.length) {
			throw new ConcurrentModificationException();
		}
		while (i != size && modCount == expectedModCount) {
			consumer.accept((E) elementData[i++]);
		}
		// update once at end of iteration to reduce heap write traffic
		cursor = i;
		lastRet = i - 1;
		checkForComodification();
	}

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

通过查看代码发现如果 modCount和expectedModCount不相等就会导致抛出异常。

modCount是修改记录数,expectedModCount是期望修改记录数,初始化时expectedModCount=modCount。

ArrayList集合的add和remove操作都有对modCount++操作,就会导致expectedModCount和modCount值不相等从而产生ConcurrentModificationException异常

4、解决方案

**解决方案1:**使用iterator的remove操作替代ArrayList集合自己的remove操作

public class ArrayListTest {

    public static void main(String[] args) {
        List<String> lists = new ArrayList<>();
        lists.add("a");
        lists.add("b");

        Iterator<String> iterator = lists.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            if (next == "b") {
                iterator.remove();
            }
        }
    }
}

分析:通过查看iterator的remove方法发现,其实还是调用了ArrayList集合的remove方法移除元素,但是会使

expectedModCount=modeCount所以不会抛出ConcurrentModificationException异常

解决方案2:使用JUC concurrent包中CopyOnWriteArrayList并发集合类

public class ArrayListTest {

    public static void main(String[] args) {
        List<String> lists = new CopyOnWriteArrayList<>();
        lists.add("a");
        lists.add("b");

        Iterator<String> iterator = lists.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            if (next == "b") {
                lists.remove(next);
                System.out.println(lists);
            }
        }
    }
}

因为迭代器中没有checkForComodification操作,并且集合的add和remove方法中都通过ReentrantLock加锁保证并发操作下的安全性。

5、优化建议

在对ArrayList集合进行并发操作时尽量使用CopyOnWriteArrayList集合类代替

### ConcurrentModificationException 异常的原因及解决方案 #### 异常原因 `java.util.ConcurrentModificationException` 是 Java 中的一种运行时异常,通常发生在使用迭代器(Iterator)遍历集合对象(如 `ArrayList`、`HashMap` 等)的过程中。当集合的结构被非迭代器自身的方法修改时(例如通过 `add` 或 `remove` 方法),就会抛出此异常[^2]。 这种异常的核心机制在于迭代器的 **fail-fast** 特性。在遍历过程中,迭代器会检查集合的修改计数器(`modCount`)是否与预期值(`expectedModCount`)一致。如果不一致,则说明集合在遍历过程中发生了结构上的修改,从而抛出 `ConcurrentModificationException`[^4]。 以下是异常检测源码的关键部分: ```java final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } ``` 上述代码展示了迭代器在每次调用 `next()` 方法时,都会执行 `checkForComodification()` 方法以确保集合未被并发修改。如果发现修改计数器不匹配,则立即抛出异常[^1]。 #### 解决方案 为避免 `ConcurrentModificationException`,可以采用以下几种方法: 1. **使用 Iterator 的 remove 方法** 在遍历集合时,应通过迭代器自身的 `remove()` 方法来移除元素,而不是直接调用集合的 `remove()` 方法。例如: ```java List<Integer> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { list.add(i); } Iterator<Integer> iterator = list.iterator(); while (iterator.hasNext()) { Integer value = iterator.next(); if (value == 5) { iterator.remove(); // 使用迭代器的 remove 方法 } } ``` 2. **使用线程安全的集合类** 如果需要在多线程环境中操作集合,可以考虑使用线程安全的集合实现,例如 `CopyOnWriteArrayList` 或 `ConcurrentHashMap`。这些集合类通过内部机制保证了线程安全性,避免了 `ConcurrentModificationException` 的发生[^3]。 3. **使用同步块** 对于非线程安全的集合类,可以通过加锁的方式保护集合的访问和修改。例如: ```java List<Integer> list = Collections.synchronizedList(new ArrayList<>()); synchronized (list) { Iterator<Integer> iterator = list.iterator(); while (iterator.hasNext()) { Integer value = iterator.next(); if (value == 5) { iterator.remove(); } } } ``` 4. **避免在增强 for 循环中修改集合** 增强 for 循环本质上也是基于迭代器实现的,因此在循环体内直接调用集合的 `remove()` 方法会导致异常。例如以下代码会抛出异常: ```java List<Integer> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { list.add(i); } for (Integer value : list) { // 增强 for 循环 if (value == 5) { list.remove(value); // 抛出 ConcurrentModificationException } } ``` 正确的做法是改用显式的迭代器进行操作。 #### 示例代码 以下是一个完整的示例,展示如何正确处理 `ConcurrentModificationException`: ```java import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { list.add(i); } // 正确方式:使用 Iterator 的 remove 方法 Iterator<Integer> iterator = list.iterator(); while (iterator.hasNext()) { Integer value = iterator.next(); if (value == 5) { iterator.remove(); // 安全移除 } } System.out.println(list); // 输出结果:[0, 1, 2, 3, 4, 6, 7, 8, 9] } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值