如何避免map或List的ConcurrentModificationException?

700 阅读2分钟

ConcurrentModificationException 异常被抛出,因为一个线程试图读取对象,而另一个线程试图修改对象。

这种情况发生在所有的java集合API类--List,HashMap,HashTable ,LinkedHashMap

在下面的例子中

  • 创建一个字符串的ArrayList。
  • 使用Iterate创建一个迭代器,使用iterator() 方法
  • 使用hasNext()方法检查下一个元素的存在与否
  • 如果下一个元素存在,打印该元素
  • 在迭代过程中,试图根据条件检查来删除一个元素
  • 这将引发一个错误Exception in thread "main" java.util.ConcurrentModificationException

下面是一些例子

  • 单个线程正在使用迭代器读取列表,并且
  • 在迭代过程中删除一个元素会导致这个异常

java.util.Iteratoriterationmodify 一个集合的过程中抛出一个错误的故障快速迭代。

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并发修改异常

这将会发生在下面的地图和列表集合中:

  1. 在地图对象的迭代过程中,修改地图实现中任何键或值的状态(例如,)。
  2. 在一个集合类中添加/删除(Iterator.remove)对象,同时对集合的对象进行迭代。
  3. 下面的例子没有抛出这个异常,因为改变键和值以及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。