java.util.ConcurrentModificationException 出现的原因和解决办法

来源:互联网 发布:破解解压密码的软件 编辑:程序博客网 时间:2024/05/18 03:18

用iterator遍历集合时碰到java.util.ConcurrentModificationException这个异常,

下面以List为例来解释为什么会报java.util.ConcurrentModificationException这个异常,代码如下:

    public static void main(String[] args) {        List<String> saveList=new ArrayList<>();        List<String> list=new ArrayList<>();        for (int j=1; j < 100; j++) {            list.add(String.valueOf(j));        }        Iterator<String> sss=list.iterator();        while (sss.hasNext()) {            if ("50".equals(sss.next())) {                list.remove("50");            }        }    } 
就是在遍历list的时候同时对list进行删除操作,就会报出java.util.ConcurrentModificationException,下面看一下java.util. AbstractList的内部类Itr的源码:

private class Itr implements Iterator<E> {      /**      * Index of element to be returned by subsequent call to next.      */      int cursor = 0;        /**      * Index of element returned by most recent call to next or      * previous.  Reset to -1 if this element is deleted by a call      * to remove.      */      int lastRet = -1;        /**      * The modCount value that the iterator believes that the backing      * List should have.  If this expectation is violated, the iterator      * has detected concurrent modification.      */      int expectedModCount = modCount;        public boolean hasNext() {              return cursor != size();      }        public E next() {              checkForComodification(); //检测modCount和expectedModCount的值!!          try {          E next = get(cursor);          lastRet = cursor++;          return next;          } catch (IndexOutOfBoundsException e) {          checkForComodification();          throw new NoSuchElementException();          }      }        public void remove() {          if (lastRet == -1)          throw new IllegalStateException();              checkForComodification();            try {          AbstractList.this.remove(lastRet); //执行remove的操作          if (lastRet < cursor)              cursor--;          lastRet = -1;          expectedModCount = modCount; //保证了modCount和expectedModCount的值的一致性,避免抛出ConcurrentModificationException异常          } catch (IndexOutOfBoundsException e) {          throw new ConcurrentModificationException();          }      }        final void checkForComodification() {          if (modCount != expectedModCount) //当modCount和expectedModCount值不相等时,则抛出ConcurrentModificationException异常          throw new ConcurrentModificationException();      }      }  
我们可以发现,ArrayList的remove方法只是修改了modCount的值,并没有修改expectedModCount,导致modCount和expectedModCount的值的不一致性,当next()时则抛出ConcurrentModificationException异常

因此使用Iterator遍历集合时,不要改动被迭代的对象,可以使用 Iterator 本身的方法 remove() 来删除对象,Iterator.remove() 方法会在删除当前迭代对象的同时维护modCount和expectedModCount值的一致性。知道问题的原因就好办了。

这里总共提供3中方式处理这个问题:

1、新建一个saveList对象用于保存要删除的值,等遍历完后调用list的removeAll方法删除:

for(String list1 : list){            if("50".equals(list1)){                saveList.add("50");            }        }        list.removeAll(saveList);
2、使用Iterator替代增强型for循环 ,Iterator.remove()方法保证了modCount和expectedModCount的值的一致性,避免抛出ConcurrentModificationException异常。
Iterator<String> sss=list.iterator();        while (sss.hasNext()) {            if ("50".equals(sss.next())) {                sss.remove();            }        }
以上俩种方式在单线程环境下不会有问题,但是在多线程并发执行的情况下还是会出现问题,所以就引入第三种方式:使用CopyOnWriteArrayList替代ArrayList:

    public static void main(String[] args) {        List<String> list=new CopyOnWriteArrayList<>();        for (int j=1; j < 100; j++) {            list.add(String.valueOf(j));        }        Iterator<String> sss=list.iterator();        while (sss.hasNext()) {            if ("50".equals(sss.next())) {                list.remove("50");            }        }    } 


CopyOnWriteArrayList也是一个线程安全的ArrayList,其实现原理在于,每次add,remove等所有的操作都是重新创建一个新的数组,再把引用指向新的数组。
由于我用CopyOnWriteArrayList少,这里就不多讨论了,想了解可以看:Java并发编程:并发容器之CopyOnWriteArrayList

对于hashMap也有类似的情况,看如下代码:

 public static void main(String[] args) {        Map<String, String> map=new HashMap<>();        for (int i=1; i < 100; i++) {            map.put(String.valueOf(i), "testMap");        }        //将map中key的值都加上tf56前缀        for (String key : map.keySet()) {            String value=map.get(key);            map.remove(key);            map.put("tf56" + key, value);        }    }
执行结果同样会抛出:java.util.ConcurrentModificationException异常,解决的办法也是非常的简单:

用ConcurrentHashMap代替HashMap即可完美解决:

public static void main(String[] args) {        Map<String, String> map=new ConcurrentHashMap<>();        for (int i=1; i < 100; i++) {            map.put(String.valueOf(i), "testMap");        }        //将map中key的值都加上tf56前缀        for (String key : map.keySet()) {            String value=map.get(key);            map.remove(key);            map.put("tf56" + key, value);        }    }


阅读全文
0 0
原创粉丝点击