并发修改exception

我有这一小段代码,它给了我并发修改exception。 我不明白为什么我一直这样做,即使我没有看到任何并发的修改正在进行。

import java.util.*; public class SomeClass { public static void main(String[] args) { List<String> s = new ArrayList<>(); ListIterator<String> it = s.listIterator(); for (String a : args) s.add(a); if (it.hasNext()) String item = it.next(); System.out.println(s); } } 

为了避免ConcurrentModificationException ,你应该这样写代码:

 import java.util.*; public class SomeClass { public static void main(String[] args) { List<String> s = new ArrayList<String>(); for(String a : args) s.add(a); ListIterator<String> it = s.listIterator(); if(it.hasNext()) { String item = it.next(); } System.out.println(s); } } 

java.util.ListIterator允许您在迭代过程中修改列表,但不能在创build和使用它之间进行修改。

我不明白为什么我一直这样做,即使我没有看到任何并发的修改正在进行。

在创build迭代器并开始使用迭代器之间,您将参数添加到要迭代的列表中。 这是一个并发修改。

  ListIterator<String> it = s.listIterator(); for (String a : args) s.add(a); // concurrent modification here if (it.hasNext()) String item = it.next(); // exception thrown here 

在完成添加元素到列表之后创build迭代器:

  for (String a : args) s.add(a); ListIterator<String> it = s.listIterator(); if (it.hasNext()) String item = it.next(); 

从JavaDoc: for ConcurrentModificatoinException:“一个线程修改一个集合,而另一个线程迭代它通常是不可能的”。

它只是意味着如果你仍然有一个开放的迭代器,你不能修改列表,因为迭代器循环会中断。 尝试移动ListIterator<String> it = s.listIterator(); 直到for循环之后。

在修改底层列表后,您不能继续迭代迭代器。 在这里,在向s添加一些项目之前创build迭代器,然后在添加之后继续执行hasNext()next() ,导致ConcurrentModificationException

如果上述解决scheme无法正常工作。 您可以使用旧的for-loop迭代列表,同时添加新的项目。 看下面的例子:

 import java.util.*; public class SomeClass { public static void main(String[] args) { ArrayList<AClass> aList = new ArrayList<AClass>(); // we will iterate this // this will cause ConcurrentModificationException. // Since we are iterating the list, at the same time modifying it. /*for(AClass a: aList){ aList.add(someMethod(a)); }*/ // old fashion for-loop will help int limit = aList.size(); for(int i=0; ctr<limit; ++i){ AClass a = aList.get(i); aList.add(someMethod(a)); } } } 

ConcurrentModificationException 可能出现在单线程环境和multithreading环境中 。 主要的问题是所有的通用迭代器(比如ArrayList中使用的迭代器 )都是FailFast迭代器 ,当我们尝试修改一个列表时,如果一个迭代器已经迭代了这个迭代器 ,它就会失败。 解决scheme – >使用CopyOnWriteArrayList,如果需求需要这种情况,而不是使用ArrayList。

对于这个完整的演示,可以使用下面提到的代码。 我们只需要将实现从CopyOnWriteArrayList更改为ArrayList。

 import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** * @author narif * */ public class TestApp { /** * @param args */ public static void main(String[] args) { List<String> testList = new ArrayList<>(); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add(6, "abcAtindex6"); int size = testList.size(); System.out.println("The Current List (ArrayList) is: " + testList); System.out.println("The size of the List (ArrayList) is: " + size); /* Comment the below lines to get the ConcurrentModificationException */ testList = new CopyOnWriteArrayList<>(testList); for (String value : testList) { System.out.println("The Value from ForEach Loop is: " + value); /* * Concurrent modification is happening here * One iterator is iterating over the list while we are trying to add new values to * the list so the results of the iteration are undefined under these circumstances. * So teh fail fast iterators will fail and will throw the ConcurrentModificationException. */ testList.add("valueFromForLoop"); testList.add("anotherValueFromForEachLoop"); } Iterator<String> it = testList.iterator(); while (it.hasNext()) { String abc = it.next(); System.out.println(abc); testList.add("Value from Iterator1"); testList.add("Value from Iterator2"); testList.add("Value from Iterator3"); testList.add("Value from Iterator4"); } System.out.println("Did the modificationa and all after conevrting the ArrayList to CopyOnWriteArrayList."); System.out.println("Calling the method to get the new List.."); testList = new CopyOnWriteArrayList<>(getTheList(testList)); for (String value : testList) { System.out.println("The value returned from method is : " + value); } } private static List<String> getTheList(List<String> pList) { List<String> list = new CopyOnWriteArrayList<>(pList); int i = 0; for (String lValue : list) { System.out.println("The list Passed is " + list); i++; list.add("localVaueFromMethod" + i); list.removeAll(pList); } return list; } } 

对于更多inifo请按照此链接可能会有所帮助ConcurrentModificationException Java文档

这不起作用:

 LinkedList<String> linkedList = new LinkedList<String>(); ListIterator listIterator = linkedList.listIterator(); linkedList.add("aa"); linkedList.add("bb"); 

这工作:

 LinkedList<String> linkedList = new LinkedList<String>(); linkedList.add("aa"); linkedList.add("bb"); ListIterator listIterator = linkedList.listIterator(); 

了解这个让我们看一下HashMap实现的来源:

 public class HashMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable{ 

其中包含如下所示的HashIterator:

 private abstract class HashIterator { ... int expectedModCount = modCount; ... HashMapEntry<K, V> nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); .... } 

每次你创build一个迭代器:

  • 创build一个计数器expectedModCount ,并将其设置为modCount的值作为条目检查点
  • 在使用put / get(add / remove)的情况下,modCount递增
  • 迭代器的nextEntry方法是用当前modCount检查这个值,如果它们是不同的并发修改exception抛出

避免这个ü可以:

  • 将地图转换为数组(不build议用于大型地图)
  • 使用并发映射或列表类( CopyOnWriteArrayList / ConcurrentMap )
  • locking映射(这种方法消除了multithreading的好处)

这将允许您迭代并添加或删除元素,而不会引发exception

并发映射/列表迭代器是一个“弱一致”的迭代器,它永远不会抛出ConcurrentModificationExceptionexception,并保证遍历构造迭代器时存在的元素,并可能(但不能保证)反映构造之后的任何修改。

有关CopyOnWriteArrayList的更多信息

看看oracle 文档页面。

 public class ConcurrentModificationException extends RuntimeException 

当这种修改不被允许时,可以通过检测到对象的并发修改的方法抛出该exception

请注意,这个exception并不总是表明一个对象已经被不同的线程同时修改。 如果单个线程发出一系列违反对象约定的方法调用,则该对象可能会抛出此exception。 例如,如果一个线程使用快速迭代器迭代集合的时候直接修改了一个集合,迭代器将抛出这个exception

在你的情况下,你已经在创build迭代器后修改了集合,因此遇到了exception。

如果你按照Stephen C的回答改变你的代码,你将不会得到这个错误。