(源码刨析)并发修改异常: java.util.ConcurrentModificationException

Posted 我永远信仰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(源码刨析)并发修改异常: java.util.ConcurrentModificationException相关的知识,希望对你有一定的参考价值。

java.util.ConcurrentModificationException:并发修改异常。
出现的原因是使用迭代器遍历List,在遍历的过程中,对List进行了增删操作。

比如:

Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if ("world".equals(s)) {//如果两个字符串相等
       // list.add("abc");      //抛出异常:ConcurrentModificationException
        list.remove(s);//一样抛出异常
    }
}

ConcurrentModificationException异常是如何产生的?

查看源码:

//什么情况下会抛出这个异常
final void checkForComodification() {
    if (modCount != expectedModCount)  //当这两个不相等的时候
        throw new ConcurrentModificationException();
}
//这两个又是什么,继续翻源码
private class Itr implements Iterator<E> { //这是实现迭代器接口的类里。我们使用的是迭代器方式遍历

	int expectedModCount = modCount; //一个值在这里被定义
	
	/*
		modCount:实际修改集合的次数
		expectedModCount:预期修改集合的次数
	*/
	一开始他们是相等的,但是我们上面在上面使用迭代器遍历过程中让它进行了一次add操作,导致这两个值不等,所以抛出了异常。
	...

思考:

  • 如果if条件不成立,程序仍然会抛出异常吗。

    答案是不会,因为if条件不成立,那么if (modCount != expectedModCount) 也成立,就不会抛出异常

  • 如果遍历方式改为使用第二种会抛出异常吗

    答案是不会,因为get方法只有检测索引的范围是否越界,并没有做checkForComodification()方法。只有索引越界了才会抛出异常:IndexOutOfBoundsException。

  • 增强for循环内部也使用了迭代器实现,使用它也可能抛出并发修改异常

总结:

产生原因

  • 迭代器遍历的过程中,通过集合对象修改了集合中元素的长度,造成了迭代器获取元素中判断预期修改值和实际修改值不一致

  • 增强for循环内部也使用了迭代器实现,他也可能抛出并发修改异常

解决方案

  • 用for循环遍历,然后用集合对象做对应的操作即可
  • 或者使用ListIterator迭代器
    ListIterator listIt = list.listIterator();

思考:为什么ListIterator使用add方法,不会抛出并发修改异常?

查看源码

//在调用 list.listIterator()方法的时候,返回的是一个ListItr。
public ListIterator<E> listIterator() {
    return new ListItr(0);
}
//找到ListItr,查看ListItr的add方法,
private class ListItr extends Itr implements ListIterator<E> {
        ...
        ...
        public void add(E e) {
            checkForComodification();

            try {
                int i = cursor;
                ArrayList.this.add(i, e);
                cursor = i + 1;
                lastRet = -1;
                expectedModCount = modCount; //发现在执行add方法,他也会将这两个值保持一致。所以在检测的时候不会出现异常。
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    }

总结:

ListIterator使用add方法,不会抛出并发修改异常,因为它会在add方法中执行 expectedModCount = modCount;所以不会抛出并发修改异常

以上是关于(源码刨析)并发修改异常: java.util.ConcurrentModificationException的主要内容,如果未能解决你的问题,请参考以下文章

ConcurrentHashMap源码刨析(基于jdk1.7)

集合的常用线程安全实现类使用及源码分析

集合的常用线程安全实现类使用及源码分析

HashMap并发修改异常

HashMap并发修改异常

Java并发-ConcurrentModificationException原因源码分析与解决办法