ConcurrentModificationException出现原因:
1.在Java集合类中,当我们在使用iterator遍历集合对象时,当集合被改变且在使用迭代器遍历集合的时候,迭代器的next()方法将抛出ConcurrentModificationException异常。
2.如果subList()生成一个新的List对象,那么当修改原始List对象的结构时,则将抛出ConcurrentModificationException
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class ConcurrentModificationExceptionExample {
public static void main(String args[]) {
List<String> myList = new ArrayList<String>();
myList.add("1");
myList.add("2");
myList.add("3");
myList.add("4");
myList.add("5");
Iterator<String> it = myList.iterator();
while (it.hasNext()) {
String value = it.next();
System.out.println("List Value:" + value);
if (value.equals("3")) {
//由于集合被改变,所以接下来的it.next()会抛出异常
myList.remove(value);
}
}
Map<String, String> myMap = new HashMap<String, String>();
myMap.put("1", "1");
myMap.put("2", "2");
myMap.put("3", "3");
Iterator<String> it1 = myMap.keySet().iterator();
while (it1.hasNext()) {
String key = it1.next();
System.out.println("Map Value:" + myMap.get(key));
if (key.equals("2")) {
//下面一条语句,由于我们正在更新myMap中的现有键值,因此其大小没有更改,所以没有获取ConcurrentModificationException
//通过查看源码,HashMap的next方法是调用nextNode().key;获得的,而nextNode()判断修改的标志的是modCount != expectedModCount,即数量不变就为意味着没有修改
//modCount 的定义:The number of times this HashMap has been structurally modified
myMap.put("1", "4");
//如果放开注释,接下来的 it1.next()就会抛出异常
// myMap.put("4", "4");
}
}
}
}
结果:
List Value:1
List Value:2
List Value:3
Exception in thread “main” java.util.ConcurrentModificationException
at java.util.ArrayList I t r . c h e c k F o r C o m o d i f i c a t i o n ( A r r a y L i s t . j a v a : 901 ) a t j a v a . u t i l . A r r a y L i s t Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList Itr.checkForComodification(ArrayList.java:901)atjava.util.ArrayListItr.next(ArrayList.java:851)
at ConcurrentModificationExceptionExample.main(ConcurrentModificationExceptionExample.java:20)
如何避免这个异常呢?
1.在单线程下,所以iterator的remove()方法从原集合中删除一个元素
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class ConcurrentModificationExceptionExample {
public static void main(String args[]) {
List<String> myList = new ArrayList<String>();
myList.add("1");
myList.add("2");
myList.add("3");
myList.add("4");
myList.add("5");
Iterator<String> it = myList.iterator();
while (it.hasNext()) {
String value = it.next();
System.out.println("List Value:" + value);
if (value.equals("3")) {
it.remove();
}
}
System.out.println("List Value:" + myList);
Map<String, String> myMap = new HashMap<String, String>();
myMap.put("1", "1");
myMap.put("2", "2");
myMap.put("3", "3");
Iterator<String> it1 = myMap.keySet().iterator();
while (it1.hasNext()) {
String key = it1.next();
System.out.println("Map Value:" + myMap.get(key));
if (key.equals("2")) {
myMap.put("1", "4");
// myMap.put("4", "4");
it1.remove();
}
}
System.out.println("myMap:" + myMap);
}
}
执行结果:
List Value:1
List Value:2
List Value:3
List Value:4
List Value:5
List Value:[1, 2, 4, 5]
Map Value:1
Map Value:2
Map Value:3
myMap:{1=4, 3=3}
在多线程环境中避免ConcurrentModificationException
1.您可以将列表转换为数组,然后在数组上进行迭代。这种方法适用于中小型列表,但是如果列表很大,则对性能的影响很大。
2.如果使用的是JDK1.5或更高版本,则可以使用ConcurrentHashMap和CopyOnWriteArrayList类。建议使用此方法来避免并发修改异常。
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
public class AvoidConcurrentModificationException {
public static void main(String[] args) {
List<String> myList = new CopyOnWriteArrayList<String>();
myList.add("1");
myList.add("2");
myList.add("3");
myList.add("4");
myList.add("5");
Iterator<String> it = myList.iterator();
while (it.hasNext()) {
String value = it.next();
System.out.println("List Value:" + value);
if (value.equals("3")) {
myList.remove("4");
myList.add("6");
myList.add("7");
myList.remove("3");
}
}
System.out.println("List Size:" + myList);
Map<String, String> myMap = new ConcurrentHashMap<String, String>();
myMap.put("1", "1");
myMap.put("2", "2");
myMap.put("3", "3");
Iterator<String> it1 = myMap.keySet().iterator();
while (it1.hasNext()) {
String key = it1.next();
System.out.println("Map Value:" + myMap.get(key));
if (key.equals("1")) {
myMap.remove("3");
myMap.put("4", "4");
myMap.put("5", "5");
}
}
System.out.println("Map Size:" + myMap);
}
}
结果:
List Value:1
List Value:2
List Value:3
List Value:4
List Value:5
List Size:[1, 2, 5, 6, 7]
Map Value:1
Map Value:2
Map Value:4
Map Value:5
Map Size:{1=1, 2=2, 4=4, 5=5}
为什么CopyOnWriteArrayList使用iterator时修改原数据就不会报错呢?
public Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}
CopyOnWriteArrayList内部静态类
static final class COWIterator<E> implements ListIterator<E> {
/** Snapshot of the array */
private final Object[] snapshot;
/** Index of element to be returned by subsequent call to next. */
private int cursor;
//传入的是对象,所以原数据修改并不影响它
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
public boolean hasNext() {
return cursor < snapshot.length;
}
public boolean hasPrevious() {
return cursor > 0;
}
..........
如果subList()生成一个新的List对象,那么当修改原始List对象的结构时,则将抛出ConcurrentModificationException
import java.util.ArrayList;
import java.util.List;
public class ConcurrentModificationExceptionWithArrayListSubList {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Java");
names.add("PHP");
names.add("SQL");
names.add("Angular 2");
List<String> first2Names = names.subList(0, 2);
System.out.println(names + " , " + first2Names);
// names.set(1, "JavaScript");
// names.add("Oracle");
// check the output below. :)
System.out.println(names);
System.out.println(first2Names);
// Let's modify the list size and get ConcurrentModificationException
// names.add("NodeJS");
System.out.println(names); // this line throws exception
first2Names.set(1, "RabbitMQ");
// System.out.println(first2Names);
System.out.println(names);
System.out.println(first2Names);
names.add(1, "RabbitMQ2");
//下一行出现异常
first2Names.set(1, "RabbitMQ3");
}
}
结果:
[Java, PHP, SQL, Angular 2] , [Java, PHP]
[Java, PHP, SQL, Angular 2]
[Java, PHP]
[Java, PHP, SQL, Angular 2]
[Java, RabbitMQ, SQL, Angular 2]
[Java, RabbitMQ]
Exception in thread “main” java.util.ConcurrentModificationException
at java.util.ArrayList
S
u
b
L
i
s
t
.
c
h
e
c
k
F
o
r
C
o
m
o
d
i
f
i
c
a
t
i
o
n
(
A
r
r
a
y
L
i
s
t
.
j
a
v
a
:
1231
)
a
t
j
a
v
a
.
u
t
i
l
.
A
r
r
a
y
L
i
s
t
SubList.checkForComodification(ArrayList.java:1231) at java.util.ArrayList
SubList.checkForComodification(ArrayList.java:1231)atjava.util.ArrayListSubList.set(ArrayList.java:1027)
at ConcurrentModificationExceptionWithArrayListSubList.main(ConcurrentModificationExceptionWithArrayListSubList.java:34)
参考文章
CopyOnWriteArrayList