public void testMap() { HashMap temp = new HashMap<>(); temp.put("aa", 123); temp.put("bb", 123); temp.put("cc", 123); temp.put("dd", 123); for (String s : temp.keySet()) { if (s.equals("aa")) { temp.put("abcd", 123);

基本上所有的集合类都会有一个叫做快速失败的校验机制,当一个集合在被多个线程修改并访问时,就会出现ConcurrentModificationException 校验机制。它的实现原理就是我们经常提到的modCount修改计数器。 如果在读列表时,modCount发生变化则会抛出ConcurrentModificationException异常。 这与线程同步是两码事,线程同步是为了保护集合中的数据不被脏读、脏写而设置的。

首先java的foreach循环其实就是根据list对象创建一个Iterator迭代对象,用这个迭代对象来遍历list,相当于list对象中元素的遍历托管给了Iterator,你如果要对list进行增删操作,都必须经过Iterator,否则Iterator遍历时会乱,所以直接对list进行删除时,Iterator会抛出ConcurrentModificationException异常

其实,每次foreach迭代的时候都有两部操作:

  1. iterator.hasNext() //判断是否有下个元素
  2. item = iterator.next() //下个元素是什么,并赋值给上面例子中的item变量

hasNext()方法的代码如下:

public E next(){
        checkForComodification();
            E next=get(cursor);
            lastRet=cursor++;
            retun next;
        }catch(IndexOutOfBoundsException e){
            checkForComodification();
            throw new NoSuchElementException();
final void checkForComodification(){
        if(modCount!=expectedModCount)
            throw new ConcurrentModificationException();

这时候你会发现这个异常是在next方法的checkForComodification中抛出的,抛出原因是modCount != expectedModCount

  • modCount是指这个list对象从new出来到现在被修改次数,当调用List的add或者remove方法的时候,这个modCount都会自动增减;
  • expectedModCount是指Iterator现在期望这个list被修改的次数是多少次。

iterator创建的时候modCount被赋值给了expectedModCount,但是调用list的add和remove方法的时候不会同时自动增减expectedModCount,这样就导致两个count不相等,从而抛出异常。

只有当两个变量值相等时才不会报错。而 list.add()操作和 list.remove(); 操作会使 modCount++; 但是变量 expectedModCount 是内部类的 Itr 子类 的 变量,该子类的实例化方法是:list.iterator(); 实例对象时赋初始值:int expectedModCount = modCount;

所以,不允许在 while(iterator.hasNext()) 循环方法内 有list.add()操作和 list.remove();操作,否则会导致expectedModCount != modCount ,进而导致 throw new ConcurrentModificationException();

	  //解决1:使用for i形式不用foreach形式
       for (int i = 0; i < list.size(); i++) {
            if (list..equals("1")) {
                list.remove(i);
        System.out.println(list.toString());
      //解决2:迭代器形式,并且remove要采用迭代器的
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            Student next = iterator.next();
            if (next.equals("2")) {
                //在这个方法中,iterator.remove()方法,但是它多了一个操作:
                //expectedModCount = modCount;
                iterator.remove();
                //list.remove(next);
        System.out.println(list.toString());
        //解决3:使用并发容器
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

注意上面第二个解决方式:

iterator.remove();在这个方法中,删除元素实际上调用的就是list.remove()方法,但是它多了一个操作:expectedModCount = modCount;

iterator.remove();
list.remove(next);

iterator.remove()源码如下:

public void remove() {
    if (lastRet == -1)
    throw new IllegalStateException();
       checkForComodification();
    try {
    AbstractList.this.remove(lastRet);
    if (lastRet < cursor)
        cursor--;
    lastRet = -1;
    expectedModCount = modCount;
    } catch (IndexOutOfBoundsException e) {
    throw new ConcurrentModificationException();

多线程情况采用如下:

1)在使用iterator迭代的时候使用synchronized或者Lock进行同步;

2)使用并发容器CopyOnWriteArrayList代替ArrayList和Vector。

//解决方案:使用并发容器即可:
ConcurrentHashMap<String, Integer> temp = new ConcurrentHashMap<>();
 

参考文章:

https://www.cnblogs.com/yaohuiqin/p/9355874.html

https://blog.csdn.net/androidboy365/article/details/50540202/

文章目录场景复现原因分析解决方法ListMap场景复现对于list,map都有可能出现,这里主要分析list! @Test public void testList() { List&lt;String&gt; list = new ArrayList(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); list.add
一、异常产生 当我们迭代一个ArrayList或者HashMap时,如果尝试对集合做一些修改操作(例如删除元素),可能会抛出java.util.ConcurrentModificationException异常。 import java.util.Iterator; import java.util.List; public class AddRemoveListElement { public static void main(String args[]) { List&l
scala> senv.execute() org.apache.flink.client.program.ProgramInvocationException: Job failed (JobID: 91b7cc8709adb0c4a65702d85a4d09ad) at org.apache.flink.client.deployment.ClusterClientJobClientAdapter.lambda$null$6(ClusterClientJobCl
Java中的Map如果在遍历过程中要删除元素,除非通过迭代器自己的remove()方法,否则就会导致抛出ConcurrentModificationException异常。JDK文档中是这么描述的: The iterators returned by all of this class's "collection view methods" are fail-fast: if the map i...
今天在运行Android代码,发现程序崩溃了,发现错误为“java.util.ConcurrentModificationException ”,错误栈在list里面。 然后,看异常变量,为list本身错误。 笔者马上想起来了,list和vector 的区别: 1)list是非线程安全的,vector是线程安全的。 2)这造成list本身比vector操作更快,但也更不安全。 马上想到用vector替换list。 然后,发现还是有问题,提示如下: java.util.ConcurrentMod
在前面一篇文章中提到,对Vector、ArrayList在迭代的时候如果同时对其进行修改就会抛出java.util.ConcurrentModificationException异常。下面我们就来讨论以下这个异常出现的原因以及解决办法。 1、异常出现的原因 先看下面这段代码: public class Test { public static void main(String[] args) { ArrayList<Integer> list = new ArrayList
import java.util.UUID; public class ContainerNotSafeDemo { public static void main(String[] args) { List<String> list= new ArrayList<>(); for (int i = 0; i < 30; i++) {
今天在使用map并需要根据某些条件删除map元素时,自然而然想到调用Map中的remove(Object key)函数进行删除,代码如下: //遍历map,如果key<5,那么就删除此元素。 Map<Integer, Integer> users = new LinkedHashMap<Integer, Integer>(); for (Map.Entry<Integer,Integer> entry : users.entrySet()){ for (
E/AndroidRuntime(22055): java.util.ConcurrentModificationException E/AndroidRuntime(22055): at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:569) 查看了下 这 错误原因(初识): 如下代码,在遍历集合时,若对集合做add或remove操作,操作后集合长度size实际已经发生变化,而iterator迭代器遍历时未知,导致出错 Set<String> set = new HashSet<String>(); for (Ite...
java.util.concurrentmodificationexception是一个Java运行时异常,它表示在正在迭代集合时,集合被修改了。当集合被修改时,会破坏正在进行的迭代,从而导致此异常发生。 这种异常通常发生在多线程环境下,因为在多线程环境下,多个线程可能同时对集合进行操作,导致在一个线程迭代集合的同时,另一个线程修改了集合。因此,为了避免这种情况,我们需要在多线程环境下使用正确的同步机制,如使用synchronized关键字、Lock等,并确保在迭代集合时没有其他线程修改它。 除了多线程环境外,此异常也可能发生在单线程环境下,例如在使用迭代器时,如果在迭代期间添加/删除集合中的元素,则会触发此异常。 要解决java.util.concurrentmodificationexception异常,我们需要根据具体情况进行调整。可以采取以下几种方法: 1. 使用正确的同步机制来避免多线程同时修改集合。 2. 在迭代集合时,不要更改集合中的元素。如果需要更改集合,请在迭代之外操作。 3. 如果需要在迭代期间更改集合,请使用迭代器的remove()方法。 总之,java.util.concurrentmodificationexception异常发生时,我们需要检查代码并对其进行调整以避免修改正在迭代的集合。