ConcurrentModificationException异常是怎么回事 - wtstengshen/blog-page GitHub Wiki


JAVA的ConcurrentModificationException估计大家都遇到过这个异常,也都能google或者baidu一把解决问题,

但是这个异常到底是怎么回事,已下是本人查看源码之后的理解。

抛出几个问题:

1,ConcurrentModificationException什么情况下会抛出这个异常?

2,ConcurrentModificationException是否意味着Concurrent情况下才会抛出这个异常呢?

3,如何解决?


1.什么情况下会抛出这个异常

ConcurrentModificationException这个异常从字面意思上理解是并发修改异常,这个异常在JAVA的那些类里边会抛出这个异常呢,

我拔了拔JAVA的api文档,说是在使用Iterator的时候有可能会抛出这个异常,并且看文档写

See Also: Collection, Iterator, ListIterator, Vector, LinkedList, HashSet, Hashtable, TreeMap, AbstractList, Serialized Form

基本上一看,这些是JAVA里的集合,其中还有TreeMap,然后看了一下TreeMap的源码,里边抛异常的地方也是Iterator接口的实现,

所以基本上可以肯定能够使用Iterator的地方,基本上都有可能抛出这个异常;下边有已

ArrayList为例子看一下,这个异常到底是什么情况;

看了ArrayList中的实现Iterator的接口的远吗发现,有这样一段代码:

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

这个方法的作用是比较modCount 和 expectedModCount 是否相等,如果不相等,就报出ConcurrentModificationException,

  • modCount变量的文档上解释是,表示list被修改的次数,在add,remove等方法会把这个值+1
  • expectedModCount变量,是在创建ArrayList迭代器的实现对象的时候,modCount的值

也就是说比较这两个变量,能够判断ArrayList的结构是否被改变,比如是否在进行变量的时候添加或者删除了元素。 因为实现Iterator接口是一个fail-fast iterator,在进行遍历集合的时候如果集合的内容发生了变化,在进行遍历 也是不准确的了,所以会抛出这个异常。

2.只有在Concurrent并发条件下才报这个异常?

答案是no;

其实仔细看一下源码就知道,再用迭代器进行遍历集合的时候,哪怕是单线程下,只要我在遍历的过程中更改的list集合的结构,都是会抛异常的, 举个例子:

    /**
	 * 错误代码
	 */
	@Test
	public void test18() {
		List<String> lists = new ArrayList<String>();
		lists.add("1");
		for(String s : lists){
			lists.remove(s);
		}
	}

使用for-each循环,本质上是调用了,list的Iterator的实现,在循环中调用了是ArrayList的remove方法,这个是否modCount的值会+1; 这就造成了expectedModCount != modCount,所以会抛出异常,这里根本没有多线程并发的问题,所以ConcurrentModificationException这个异常其实是说,你在变量的时候,集合的结构已经发生了变化,在接着遍历,有可能就是不正确的了,并不一定是在多线程环境下才会出现。

3,如何解决?

那现在的问题是,这个解决,我想在遍历的过程中更改集合的内容,并且不抛异常:使用Iterator或者listIterator中的方法修改集合中的内容。 Iterator实现类源码中的remove方法代码实现:

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

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

这个方法为什么没有问题,就是在第10行,重新设置了expectedModCount的值和modCount相等。所以在 单线程中 使用迭代器 对集合进行修改是没有问题的。我这里把加了一个限定条件,单线程,那么多线程中如何搞这个东西呢,看下回分解。

欢迎拍砖;

作者 @悠悠小竹子

博客 iocoding

⚠️ **GitHub.com Fallback** ⚠️