此处将为大家介绍关于CopyOnWriteArrayList抛出CurrentModificationException的详细内容,并且为您解答有关copyonwritearraylistremove
此处将为大家介绍关于CopyOnWriteArrayList抛出CurrentModificationException的详细内容,并且为您解答有关copyonwritearraylist remove的相关问题,此外,我们还将为您介绍关于ArrayList remove时报ConcurrentModificationException、ArrayList中ConcurrentModificationException、ArrayList中foreach循环中增添、删除导致ConcurrentModificationException、ArrayList中的ConcurrentModificationException,并发修改异常,fail-fast机制。的有用信息。
本文目录一览:- CopyOnWriteArrayList抛出CurrentModificationException(copyonwritearraylist remove)
- ArrayList remove时报ConcurrentModificationException
- ArrayList中ConcurrentModificationException
- ArrayList中foreach循环中增添、删除导致ConcurrentModificationException
- ArrayList中的ConcurrentModificationException,并发修改异常,fail-fast机制。
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:
ConcurrentModificationException 这错误本身是为“提醒”程序员正在并发【修改Arraylist数据和循环读取Arraylist数据结构】, 因为ArrayList 不是写线程安全的数据结构,所以会报ConcurrentModificationException, Arraylist不是一个同步循环和删除为目的数据结构。
如果非要删除ArrayList里的元素,可使用iterator:
Iterator<String> iter = list.iterator();
while(iter.hasNext()){
String s = iter.next();
if(s.equals("xxx")){
iter.remove();
}
}
的方式删除。
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
一、使用背景
在阿里巴巴开发手册中,有这样一条规定:不要在foreach循环里进行add和remove操作(这里指的是List的add和remove操作),否则会抛出ConcurrentModificationException。remove元素请使用iterator。
二、源码
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类的时候会给他赋初始值,只有通过该迭代器进行值的增删才会修改该值
2.iterator.next();
①在调用迭代器的next方法时,他会进行检查,比较modCount和expectedModCount的值,如果不相等,Concurrent
四、总结
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机制。
一:什么时候出现?
当我们用迭代器循环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抛出CurrentModificationException和copyonwritearraylist remove的分享已经告一段落,感谢您的关注,如果您想了解更多关于ArrayList remove时报ConcurrentModificationException、ArrayList中ConcurrentModificationException、ArrayList中foreach循环中增添、删除导致ConcurrentModificationException、ArrayList中的ConcurrentModificationException,并发修改异常,fail-fast机制。的相关信息,请在本站查询。
本文标签: