GVKun编程网logo

CopyOnWriteArrayList抛出CurrentModificationException(copyonwritearraylist remove)

7

此处将为大家介绍关于CopyOnWriteArrayList抛出CurrentModificationException的详细内容,并且为您解答有关copyonwritearraylistremove

此处将为大家介绍关于CopyOnWriteArrayList抛出CurrentModificationException的详细内容,并且为您解答有关copyonwritearraylist remove的相关问题,此外,我们还将为您介绍关于ArrayList remove时报ConcurrentModificationException、ArrayList中ConcurrentModificationException、ArrayList中foreach循环中增添、删除导致ConcurrentModificationException、ArrayList中的ConcurrentModificationException,并发修改异常,fail-fast机制。的有用信息。

本文目录一览:

CopyOnWriteArrayList抛出CurrentModificationException(copyonwritearraylist remove)

CopyOnWriteArrayList抛出CurrentModificationException(copyonwritearraylist remove)

ConcurrentModificationException当我遍历列表时,偶尔会得到一个。Google搜索通知我,这可能是因为我在迭代该列表时正在另一个线程中更改该列表,并且要使此问题消失,我应该使用java.util.concurrent.CopyOnWriteArrayList…。

…除了我已经是。

显然,我在某处正在做一些 非常 愚蠢的事情。

是否有人对如何引发CopyOnWriteArrayList抛掷物有任何见识ConcurrentModificationException?如果有关系,我正在使用Java
5。

编辑: 由于我使用的转换器可能很重要,因此我以两种方式修改此列表:

  • 在前面添加元素。(list.add(0, newElement);
  • 使用subList让较旧的项目掉下来。(list = list.subList(0, MAX_LIST_SIZE);

那些举起红旗吗?如果是这样,为什么?我的理解是,因为这些操作首先复制了事物,所以任何现有的迭代器都将指向未修改的原始对象,因此将不在乎。我的知识有漏洞吗?

编辑2: 导致问题的精确代码仍然有点模糊,但是我至少可以发布我看到的异常:

java.util.ConcurrentModificationException    at java.util.concurrent.CopyOnWriteArrayList$COWSubList.checkForComodification(Unknown Source)    at java.util.concurrent.CopyOnWriteArrayList$COWSubList.iterator(Unknown Source)    at....

…它指向我的代码中的for-each循环实例。

COWSubList似乎暗示着我的呼吁subList是问题的根源。我仍然想了解为什么。

编辑3:* facepalm ***

CopyOnWriteArrayList.subList()返回一个List不是
一个CopyOnWriteArrayList。它返回的列表不承担任何COWAL保护的隐含义务。这使得subList()像这样删除元素是一个非常糟糕的主意。

不确定这是否是我的罪魁祸首,但该死的可疑之处无论如何都需要纠正。

答案1

小编典典

如果包含列表从其下方更改,则CopyOnWriteArrayList.subLists会引发ConcurrentModificationExceptions:

public class ListTest {  private static List<int[]> intList;  public static void main (String[] args) {    CopyOnWriteArrayList<Integer> cowal = new CopyOnWriteArrayList<Integer>();    cowal.add(1);    cowal.add(2);    cowal.add(3);    List<Integer> sub = cowal.subList(1, 2);    cowal.add(4);    sub.get(0); //throws ConcurrentModificationException  }}

ArrayList remove时报ConcurrentModificationException

ArrayList remove时报ConcurrentModificationException

ArrayList remove时报ConcurrentModificationException:

ConcurrentModificationException 这错误本身是为“提醒”程序员正在并发【修改Arraylist数据和循环读取Arraylist数据结构】, 因为ArrayList 不是写线程安全的数据结构,所以会报ConcurrentModificationException, Arraylist不是一个同步循环和删除为目的数据结构。


如果非要删除ArrayList里的元素,可使用iterator

  1. Iterator<String> iter = list.iterator();  

  2. while(iter.hasNext()){  

  3.     String s = iter.next();  

  4.     if(s.equals("xxx")){  

  5.         iter.remove();  

  6.     }  

  7. }  

的方式删除。

ArrayList中ConcurrentModificationException

ArrayList中ConcurrentModificationException

java中两种基本的集合结构ArrayList和LinkedList底层有两种不同的存储方式实现,ArrayList为数组实现,属于顺序存储,LinkedList为链表实现,属于链式存储,在对ArrayList做迭代删除时,会出现ConcurrentModificationException

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

        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            list.remove(next);
        }
    }
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.baicells.linked.list.ListTest.main(ListTest.java:23)

但如果在list再添加一个元素,如bb,此时list.size = 2,上述代码运行结束后,list中只有bb,虽然与预期结果不一致,但并没有出现ConcurrentModificationException,当再次向集合中添加更多元素时,又出现了ConcurrentModificationException,使用的jdk版本为1.8.

ArrayList源码中,方法iterator()返回了Itr对象

/**
     * Returns an iterator over the elements in this list in proper sequence.
     *
     * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
     *
     * @return an iterator over the elements in this list in proper sequence
     */
    public Iterator<E> iterator() {
        return new Itr();
    }

Itr为ArrayList内部类,主要关注hasNext,next,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在add和remove时都会执行++操作,这个可在ArrayList源码中找到出处

在Ite类中,将expectedModCount 的大小初始化为modCount,当执行hashNex和nex时,都不会使modCount的值发生变化

当list中只有一个数据时,此时cursor=0,size=1,hasNex返回true,在next方法中校验modCount和expectedModCount是否相等,如果不相等,则抛出并发修改异常,如果相等,对cursor做了+1操作

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

此时两者是相等的,都为1,然后执行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 remove method that skips bounds checking and does not
     * return the value removed.
     */
    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; // clear to let GC do its work
    }

remove是对modCount做了++操作,并且对size做了--操作,while循环继续,hasNext,cursor=1,size=0,两者不相等,则进入到循环,执行next操作

此时modCount在上述remove操作时已经做了++操作,expectedModCount的值却没有变化,故modCount和expectedModCount是不相等的,因此抛出ConcurrentModificationException

当输入两个参数时为什么就不会报错了,虽然结果不是预期的清空了集合?

当集合为2时,在第一次删除后,各关键属性值分别为cursor=1,modCount在add两次后变为2,在remove一次后变为3,expectedModCount=2,size在remove后--,变为1,故此时在while循环hasNext中对比cursor!=size,返回false,while循环结束,继续向下走,所以最后集合中剩下了第二次add的结果,第一次add结果被删除,程序也没有出现ConcurrentModificationException异常。

当输入三个参数或者更多时,会怎样?

继续按照上述思路分析,当集合中有三个元素时,在第一次删除后,各关键属性值分别为cursor=1,modCount在add三次后变为3,在remove一次后变为4,expectedModCount=3,size在remove后--,变为2,此时while循环中cursor!=size返回true,进入while循环,next方法中检测到modCount != expectedModCount返回false,则抛出ConcurrentModificationException

当为更多元素时,在第二次进入到next方法后,都将抛出ConcurrentModificationException,只有在数组元素个数为2时,才不会发生ConcurrentModificationException,但结果也不是我们预期的

 

综上,不要在迭代集合时删除元素,即使是foreach或者普通for循环(普通for循环或者foreach也会造成size--),也不要这么做,这样做可能造成我们意想不到错误。

 

ArrayList中foreach循环中增添、删除导致ConcurrentModificationException

ArrayList中foreach循环中增添、删除导致ConcurrentModificationException

一、使用背景

在阿里巴巴开发手册中,有这样一条规定:不要在foreach循环里进行add和remove操作(这里指的是List的add和remove操作),否则会抛出ConcurrentModificationException。remove元素请使用iterator。

f29f0bec6c933e9d6fb3fb414219ef4.png

二、源码

1.我们知道foreach是语法糖,他本质还是iterator进行的循环,因此下面的代码和使用foreach循环是一样的。在循环里面我们使用“错误”操作,使用List的add方法进行操作,会抛出ConcurrentModificationException

       ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("apple");
        Iterator<String> iterator = arrayList.iterator();
        while(iterator.hasNext()){
            String value = iterator.next();
            if("apple".equals(value)){
                arrayList.add("orange");
            }
        }

三、源码解析

1.arrayList.iterator();

①返回Itr类,并将modcount的值赋值给一个变量expectedModCount,其中modcount表示List实际被增删的次数,expectedModCount表示该迭代器期望被增删的次数,当新建Itr类的时候会给他赋初始值,只有通过该迭代器进行值的增删才会修改该值

1f94f5447debd05c92a2ce77cf2bf53.png

0f9094193aa7672ddd5392485494233.png

2.iterator.next();

①在调用迭代器的next方法时,他会进行检查,比较modCount和expectedModCount的值,如果不相等,Concurrent

a06c77dae6f16bf2838a90466954f5c.png

05b5c8ca957494affd4157961ac3763.png

四、总结

1.modCount和expectedModeCount不一致才会抛出ConcurrentModificationException。当我们调用List的remove方法时,他只会修改modCount的值;当我们调用iterator的remove方法,他会将modCount的值赋值给expectedModeCount

2.modCount和expectedModeCount是属于fast-fail机制,用于多线程中,当进行遍历的时候,有其他线程修改值的时候就会进行检查

五、解决方法

1.使用普通for循环进行操作

2.在循环中使用iterator进行操作

3.使用stream流进行过滤

4.使用fast-saft安全的类,如ConCurrentLinkedQueue

ArrayList中的ConcurrentModificationException,并发修改异常,fail-fast机制。

ArrayList中的ConcurrentModificationException,并发修改异常,fail-fast机制。

一:什么时候出现?

  当我们用迭代器循环list的时候,在其中用list的方法新增/删除元素,就会出现这个错误。

package com.sinitek.aml;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ConcurrentModificationExceptionDemo {


    public static void main(String[] args) {


        List<String> arrayList = new ArrayList<>();

        arrayList.add("a");
        arrayList.add("b");
        arrayList.add("c");


        Iterator<String> iterator = arrayList.iterator();
        while(iterator.hasNext()){
            String tmp = iterator.next();
            if ("a".equals(tmp)){
                arrayList.remove(tmp);

            }
        }




    }
}

 

二:为什么会出这个错误? 

 如上图所示,运行代码就会有这个错误,我们看一下堆栈。

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.sinitek.aml.ConcurrentModificationExceptionDemo.main(ConcurrentModificationExceptionDemo.java:22)

  

  可以看到,是在ArrayList里面的Itr内部类里的checkForModification()方法里,抛出的这个异常,我们看下源码。

final void checkForComodification() {
            //如果修改的次数 和 预期修改的次数不一致,就会报错
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }

 

  可以看到,上面两个关键的值是:modCount和expectModCount,其中的modCount来自ArrayList的父类,AbstractList,有兴趣的可以看下上面的翻译,也说明了这个字段的作用,正是为了fail-fast(快速失败)设计的。

  该值会在new出对象的时候,初始化为0;并且在每次的修改/删除操作时自增。

   

//arrayList的add方法,会先调用一次检查容量是否够的操作,我们可以看到,jdk专门在里面加了注释,会 Increments modCount, 增加modCount的值
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    //确保新增元素容量大小够用的方法
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++; //首先先自增一次
    
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)   //判断最小需要的容量是否大于数组的长度(这里有个疑问,为什么一定相减然后判断是否>0,直接比较会有溢出的问题需要考虑么)
            grow(minCapacity);  //扩容
    }

    //删除方法,也会有modCount++的操作
    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;
    }

  

那我们看一下,expectedModCount时怎么变化的。

  在ArrayList的内部类Itr(这个命名是不是有点随意了)里面,会看到expectedModCount是作为成员变量一起初始化的,所以我们在最开始的时候,调用list.iterator()方法的时候,就会初始化expectedModCount,将他变成和modCount一样的值。所以问题出在:

    1. new ArrayList,新增后,modCount会有一个初始值0。

    2. 调用iterator()方法后,会把expectedModCount的值初始化为modCount的值,此时,这两个值是一致的。

    3. 如果在循环里,使用list对象新增/删除对象(本文最上面所作的操作),modCount就会自增变化,而此时,expectedModCount却不会改变。

    4. 调用iterator里面的next() -> checkForComodification(),判断modCount==expectModCount的时候,就会发现不一致,从而抛出这个异常。

 

三:怎么解决。

  有两种方法。

    • 使用Itr类中的remove方法移除,但是如果用的是从Iterable继承的iterator返回的Iterator是没有add()方法的接口的,如果需要add(),那么需要调用list专有的迭代器listInterator()方法返回的ListIterator().
    • 不适用迭代器,使用原生的循环方式,这样就可以避免这个异常。(增强for只是一个语法糖(Syntactic Sugar)本质上还是使用了迭代器,也会有这个异常)。

 

四:为什么要定义这个异常?

    凡事多问一个为什么,jdk为什么要怎么设计,很简单,其实从命名上就能看出来,当我们在循环的时候,无论是我们在迭代循环的时候(目前我们就是这种情况),或者是其他的线程修改了这个(这个应该是jdk主要想解决的),都会出现数据不一致的情况,所以需要这个异常,让你fail-fast,早点异常早点再次尝试等。

  Q:为什么不在add()方法的时候,同时修改expectModCount,这样就他们一致了不就行了?

  A:add()方法在调用的时候,有可能没有这个Itr这个对象,而且单单修改值是没有用的,需要把对应的迭代器内部保存数据的值也对应的修改,只修改expectModCount有点类似掩耳盗铃。

  Q:那jdk可以新建一个重载方法,让itr对象当作参数,把他传进来,修改对应的保存数据的数组和对应的expectModCount,这样就能保证两个都一致了把。

  A:确实,这种方法对于上文这种fail-fast确实适用,但是还有一种情况就是:可能修改list的线程和迭代的线程都不是同一个,所以根本就没有itr这个对象,也就无法修改这个值了。

我们今天的关于CopyOnWriteArrayList抛出CurrentModificationExceptioncopyonwritearraylist remove的分享已经告一段落,感谢您的关注,如果您想了解更多关于ArrayList remove时报ConcurrentModificationException、ArrayList中ConcurrentModificationException、ArrayList中foreach循环中增添、删除导致ConcurrentModificationException、ArrayList中的ConcurrentModificationException,并发修改异常,fail-fast机制。的相关信息,请在本站查询。

本文标签: