当前位置:   article > 正文

以ArrayList源码为例,说说为什么会出现ConcurrentModificationException_concurrentmodification arraylist

concurrentmodification arraylist

主要靠两个属性:

  • 一个是数组对象属性modCount
  • 一个是数据迭代器对象属性expectedModCount

每次对数组增加或删除元素的时候(注意修改的时候是没有的,这个属性只关注数组长度是否变化)会对数组对象属性modCount做加一操作。
当我们创建一个迭代器对象时,迭代器对象会取当前数组对象属性modCount属性作为默认值存储到迭代器对象里面。
每次迭代器对象往前迭代的时候都要拿自己的对象里面expectedModCount属性跟数组对象的modCount做一次比较,如果相同则代表数据没被改过。如果不同则抛出ConcurrentModificationException。

注意:这里只能解决在遍历时,不能对数据做增加或删除修改,对多线程并不是安全的。
下面看下代码。

...
protected transient int modCount = 0;
...
  • 1
  • 2
  • 3

看下这个属性主要在哪些地方会被使用

	//以删除为例子
	public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }
	
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

OK,下面先跑出异常

    public static void main(String[] args) {
        List<Integer> a = new ArrayList<>();
        a.add(1);
        a.add(1);
        a.add(1);
        a.add(1);
        a.add(1);
        for(Integer n:a){
            a.remove(0);
        }
    }
//
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
	at java.util.ArrayList$Itr.next(ArrayList.java:851)
	at bingo.galink.ability.boot.Test.main(Test.java:18)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

直接定位到迭代器对象的checkForComodification方法

    final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
  • 1
  • 2
  • 3
  • 4

OK,在这里我们就可以看到当迭代器对象里面expectedModCount跟当前数组对象的modCount不一样时(remove的时候改变了modCount),就会出现这个异常。
那解决的方法也很简单,就是避免在多线程下、在for-in语法下对同一数组做遍历删除操作。
使用迭代器里面访问,使用迭代器的remove方法进行删除

public static void main(String[] args) {
        List<Integer> a = new ArrayList<>();
        a.add(1);
        a.add(1);
        a.add(1);
        a.add(1);
        a.add(1);
        Iterator<Integer> i = a.iterator();
        while (i.hasNext()){
            i.next();
            i.remove();
        }
//        for(Integer n:a){
//            a.remove(0);
//        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

最后再看看为什么这样是行的

        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();
            }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

此时,访问数组对象只有迭代器自己,所以先做了checkForComodification判断,肯定是成功的,因为数组对象的modCount并没有改变。然后再通过迭代器本身去删除值,并修改迭代器对象的内部属性。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/248161?site
推荐阅读
相关标签
  

闽ICP备14008679号