我有这段小代码,它给了我并发修改异常。我无法理解为什么我一直得到它,即使我没有看到任何同时进行的修改。
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
允许您在迭代期间修改列表,但不能在创建和使用它之间修改列表。
我无法理解为什么我一直得到它,即使我没有看到任何同时进行的修改。
在创建迭代器和开始使用迭代器之间,您向要迭代的列表添加了参数。这是一个并发修改。
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
在完成向列表中添加元素之后创建迭代器:
for (String a : args)
s.add(a);
ListIterator<String> it = s.listIterator();
if (it.hasNext())
String item = it.next();
来自JavaDoc:的ConcurrentModificatoinException:“一个线程修改一个集合而另一个线程迭代它时通常不可能”。
它只是意味着如果你仍然有一个打开的迭代器,则不允许修改列表,因为迭代器循环会中断。尝试移动ListIterator<String> it = s.listIterator();
直到for循环。
修改基础列表后,不允许继续迭代迭代器。在这里你创建迭代器,然后向s
添加几个项目,然后在添加之后继续在它上面做一个hasNext()
和一个next()
,导致ConcurrentModificationException
如果上述解决方案无法正常工作。您可以使用旧的for循环来迭代List,同时添加新项。请参阅以下示例:
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可能出现在单线程环境和多线程环境中。主要的问题是所有通用迭代器(如ArrayList中使用的迭代器)都是FailFast迭代器,当我们尝试修改一个列表时,如果一个迭代器已经迭代它,它就会失败。解决方案 - >如果需求需要此类方案而不是使用ArrayList,请使用CopyOnWriteArrayList。
有关此的完整演示,可以使用下面提到的代码。我们只需要将实现从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;
}
}
欲了解更多信息,请点击此链接,这可能对qazxsw poi很有帮助
这不起作用:
ConcurrentModificationException Java Docs
这有效:
LinkedList<String> linkedList = new LinkedList<String>();
ListIterator listIterator = linkedList.listIterator();
linkedList.add("aa");
linkedList.add("bb");
要了解这一点,我们来看看HashMap实现的来源:
LinkedList<String> linkedList = new LinkedList<String>();
linkedList.add("aa");
linkedList.add("bb");
ListIterator listIterator = linkedList.listIterator();
其中包含HashIterator如下:
public class HashMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable{
每次创建迭代器时:
为了避免这种情况你可以:
private abstract class HashIterator {
...
int expectedModCount = modCount;
...
HashMapEntry<K, V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
....
}
/ CopyOnWriteArrayList)这将允许您同时迭代和添加或删除元素,而不会引发异常
并发映射/列表迭代器是一个“弱一致”的迭代器,它永远不会抛出ConcurrentModificationException,并保证遍历构造迭代器时存在的元素,并且可能(但不保证)反映构造之后的任何修改。
看看oracle More info on CopyOnWriteArrayList 页面。
documentation
当不允许这样的修改时,检测到对象的并发修改的方法可能抛出此异常
请注意,此异常并不总是表示某个对象已被另一个线程同时修改。如果单个线程发出一系列违反对象契约的方法调用,则该对象可能会抛出此异常。例如,如果线程在使用失败快速迭代器迭代集合时直接修改集合,则迭代器将抛出此异常。
在您的情况下,您在创建迭代器后修改了集合,因此您遇到了异常。
如果您根据public class ConcurrentModificationException
extends RuntimeException
答案更改代码,则不会出现此错误。