ConcurrentModificationException
异常被抛出,因为一个线程试图读取对象,而另一个线程试图修改对象。
这种情况发生在所有的java集合API类--List
,HashMap
,HashTable
,LinkedHashMap
在下面的例子中
- 创建一个字符串的ArrayList。
- 使用Iterate创建一个迭代器,使用
iterator()
方法 - 使用hasNext()方法检查下一个元素的存在与否
- 如果下一个元素存在,打印该元素
- 在迭代过程中,试图根据条件检查来删除一个元素
- 这将引发一个错误
Exception in thread "main" java.util.ConcurrentModificationException
下面是一些例子
- 单个线程正在使用迭代器读取列表,并且
- 在迭代过程中删除一个元素会导致这个异常
java.util.Iterator
在 iteration
和modify
一个集合的过程中抛出一个错误的故障快速迭代。
import java.util.ArrayList;
import java.util.Iterator;
public class App {
public static void main( String[] args ){
ArrayList list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("four");
list.add("five");
Iterator strIterator = list.iterator();
while (strIterator.hasNext()) {
String nextString = strIterator.next();
System.out.println("Current Object: " + nextString);
if (nextString.equals("five"))
list.remove(nextString);
}
}
}
输出
Current Object: one
Current Object: two
Current Object: three
Current Object: four
Current Object: five
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at App.main(App.java:15)
Process finished with exit code 1
在迭代过程中,不可能用list.remove()
方法从数组列表中移除一个元素。list.remove()方法在迭代过程中移除一个元素是不安全的。
这是对java list的ConcurrentModificationException的一个解决方案或修复。
你可以使用java.util.Iterator
中的remove()
方法来代替list.remove()
Iterator.remove是在迭代过程中改变集合的安全方法。
import java.util.ArrayList;
import java.util.Iterator;
public class App {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
Iterator strIterator = list.iterator();
while (strIterator.hasNext()) {
String nextString = strIterator.next();
System.out.println("Current Object: " + nextString);
if (nextString.equals("five"))
strIterator.remove();
}
System.out.println(list);
}
}
HashMap并发修改异常
这将会发生在下面的地图和列表集合中:
- 在地图对象的迭代过程中,修改地图实现中任何键或值的状态(例如,)。
- 在一个集合类中添加/删除(Iterator.remove)对象,同时对集合的对象进行迭代。
- 下面的例子没有抛出这个异常,因为改变键和值以及hashhmap的大小没有改变,这是一个在hashhmap的迭代过程中修改集合的例子。
迭代有三种方式 -iterator
,map.entrySet
与循环。
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class App {
public static void main(String[] args) {
HashMap mapDemo = new HashMap<>();
mapDemo.put("key-1", "value-1");
mapDemo.put("key-2", "value-2");
mapDemo.put("key-3", "value-3");
Iterator iterator = mapDemo.keySet().iterator();
while(iterator.hasNext()) {
String key = iterator.next();
System.out.println("Map Value:" + mapDemo.get(key));
if (key.equals("key-2")) {
mapDemo.put("key-2", "newvalue");
}
}
for (Map.Entry entry : mapDemo.entrySet()) {
if (entry.getKey().contains("key-2")) {
entry.setValue("new Value-2");
}
}
for (Map.Entry entry : mapDemo.entrySet()) {
System.out.println(entry.getKey() + "===" + entry.getValue());
}
}
}
输出
Map Value:value-1
Map Value:value-3
Map Value:value-2
key-1===value-1
key-3===value-3
java8在集合类中引入了removeIf
方法。
import java.util.ArrayList;
public class App {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
System.out.println(list);
list.removeIf(item -> item == "five");
System.out.println(list);
}
}
[one, two, three, four, five]
[one, two, three, four]
ConcurrentModificationException
在 和 应用程序中抛出。single threaded
multi threaded
- 你可以使用
ConcurrentHashMap
,ConcurrentSkipListMap
,ConcurrentLinkedQueue
,CopyOnWriteArrayList
,CopyOnWriteArrayList
类,从java7版本的java.util.concurrent
模块开始。 - 你可以使用Iterator.remove来在迭代过程中从集合中删除一个对象。
- 你也可以通过在集合上加锁来使用synchronize块,从而降低性能,但使用它并不安全。
- 你也可以使用
removeIf
方法来实现。
总结
总而言之,我们已经看到了在迭代过程中修改对象时出现的ConcurrentModifiedException,并为List和Map指定了解决方案,使用java8。