我从一些教程中理解了这个概念,只要我知道当一个线程在迭代列表时,其他线程就可以修改底层列表而我们不会得到ConcurrentModificationException(CME)但是在ArrayList的情况下我们将获得CME。
但在下面的程序中只有一个线程(主线程),但我仍然得到CME ..为什么?
是因为迭代器?
如果我用COWAL替换AL而不是我没有得到任何异常,但我也没有“D”元素..为什么?
AL<String> l=new AL<>();
l.add("a");
l.add("b");
l.add("c");
Iterator<String> itr=l.iterator();
l.add("d");
while(itr.hasNext())
{
String s=itr.next();
Sop(s);
}
你得到ConcurrentModificationException
因为ArrayList
的迭代器在设计上是快速失败的。这意味着一旦创建了迭代器,如果修改了ArrayList
(添加或删除元素),它将抛出ConcurrentModificationException
。
如果你检查异常日志语句,它会被String s=itr.next();
方法抛出到itr.next()
行,因为迭代器的next()
方法通过使用checkForComodification()
变量调用ArrayList
大小的modCount
方法检查修改,它在从列表创建迭代器时复制它。
现在让我们谈谈你没有得到这个例外的CopyOnWriteArrayList
是因为CopyOnWriteArrayList
是ArrayList
的线程安全的变量,其中所有的变量操作,如add,remove,set都是通过将内部数组复制到新数组并用以下内容替换旧数组来实现的。新创建的。
因此,当您从列表中获取迭代器时,它将保存数组的引用,并且当您向列表中添加元素时,该列表将具有完全新的数组。并且迭代器仍然指向旧数组。
您可能已经注意到,语句l.add("d");
中新添加的元素未在控制台上打印。但如果你打印整个清单,它就在那里。
以下是CopyOnWriteArrayList
的示例代码:
List<String> l = new CopyOnWriteArrayList<>();
l.add("a");
l.add("b");
l.add("c");
Iterator<String> itr = l.iterator();
l.add("d");
while (itr.hasNext()) {
String s = itr.next();
System.out.println(s);
}
System.out.println(l);
输出产生的是:
a
b
c
[a, b, c, d]
希望这可以帮助。请享用 :)
错误是因为行
l.add("d");
在创建Iterator之后,您正在修改列表结构(添加或删除元素)。
当你打电话的时候
itr.next();
在内部,它检查列表中的任何结构更改(在ArrayList中添加或删除),并且它发现添加了一个元素,因此数组列表的大小已更改。这就是您获得该异常的原因。
如果要在创建Iterator之前添加该元素,或者在使用迭代器之后添加该元素,则不会出现此错误。
下面是ArrayList Class中next()的代码;
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
......
return (E) elementData[lastRet = i];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}