java.util.ConcurrentModificationException异常分析

Java在操作ArrayList、HashMap、TreeMap等容器类时,遇到了java.util.ConcurrentModificationException异常。以ArrayList为例,如下面的代码片段:

[java] view plaincopy

  1. import java.util.ArrayList;
  2. import java.util.Iterator;
  3. import java.util.List;
  4. import java.util.concurrent.CopyOnWriteArrayList;
  5. public class Test {
  6. public static void main(String[] args){
  7. List<String> strList = new ArrayList<String>();
  8. strList.add("string1");
  9. strList.add("string2");
  10. strList.add("string3");
  11. strList.add("string4");
  12. strList.add("string5");
  13. strList.add("string6");
  14. // 操作方式1:while(Iterator);报错
  15. Iterator<String> it = strList.iterator();
  16. while(it.hasNext()) {
  17. String s = it.next();
  18. if("string2".equals(s)) {
  19. strList.remove(s);
  20. }
  21. }
  22. // 解决方案1:使用Iterator的remove方法删除元素
  23. // 操作方式1:while(Iterator):不报错
  24. //      Iterator<String> it = strList.iterator();
  25. //      while(it.hasNext()) {
  26. //          String s = it.next();
  27. //          if("string2".equals(s)) {
  28. //              it.remove();
  29. //          }
  30. //      }
  31. // 操作方式2:foreach(Iterator);报错
  32. //      for(String s : strList) {
  33. //          if("string2".equals(s)) {
  34. //              strList.remove(s);
  35. //          }
  36. //      }
  37. // 解决方案2:不使用Iterator遍历,注意索引的一致性
  38. // 操作方式3:for(非Iterator);不报错;注意修改索引
  39. //      for(int i=0; i<strList.size(); i++) {
  40. //          String s = strList.get(i);
  41. //          if("string2".equals(s)) {
  42. //              strList.remove(s);
  43. //              strList.remove(i);
  44. //              i--;// 元素位置发生变化,修改i
  45. //          }
  46. //      }
  47. // 解决方案3:新建一个临时列表,暂存要删除的元素,最后一起删除
  48. //      List<String> templist = new ArrayList<String>();
  49. //      for (String s : strList) {
  50. //          if(s.equals("string2")) {
  51. //              templist.add(s);
  52. //          }
  53. //      }
  54. //      // 查看removeAll源码,其使用Iterator进行遍历
  55. //      strList.removeAll(templist);
  56. // 解决方案4:使用线程安全CopyOnWriteArrayList进行删除操作
  57. //      List<String> strList = new CopyOnWriteArrayList<String>();
  58. //      strList.add("string1");
  59. //      strList.add("string2");
  60. //      strList.add("string3");
  61. //      strList.add("string4");
  62. //      strList.add("string5");
  63. //      strList.add("string6");
  64. //      Iterator<String> it = strList.iterator();
  65. //      while (it.hasNext()) {
  66. //          String s = it.next();
  67. //           if (s.equals("string2")) {
  68. //               strList.remove(s);
  69. //          }
  70. //      }
  71. }
  72. }

执行上述代码后,报错如下:

[plain] view plaincopy

  1. <span style="font-size:14px;">Exception in thread "main" java.util.ConcurrentModificationException
  2. at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
  3. at java.util.ArrayList$Itr.next(Unknown Source)
  4. at concurrentModificationException.Test.main(Test.java:21)
  5. </span>

在第21行报错,即it.next(),迭代器在获取下一个元素时报错。找到java.util.ArrayList第830行,看到it.next()的源代码,如下:

[java] view plaincopy

  1. <span style="font-size:14px;">        @SuppressWarnings("unchecked")
  2. public E next() {
  3. checkForComodification();
  4. int i = cursor;
  5. if (i >= size)
  6. throw new NoSuchElementException();
  7. Object[] elementData = ArrayList.this.elementData;
  8. if (i >= elementData.length)
  9. throw new ConcurrentModificationException();
  10. cursor = i + 1;
  11. return (E) elementData[lastRet = i];
  12. }</span>

调用了checkForComodification()方法,代码如下:

[java] view plaincopy

  1. <span style="font-size:14px;">        final void checkForComodification() {
  2. if (modCount != expectedModCount)
  3. throw new ConcurrentModificationException();
  4. }</span>

即,比较modCount和expectedModCount两个是否相等。modCount是ArrayList从AbstractList继承来的属
性,查看modCount属性的doc文档,可知,modCount表示列表(list)被结构性修改(structurally
modified)的次数。structurally
modified是指造成列表中元素个数发生变化的操作,ArrayList中的add,addAll,remove,
fastRemove,clear, removeRange,  ensureCapacity,
 ensureCapacityInternal,
 ensureExplicitCapacity等方法都会使modCount加1,而batchRemove,removeAll,retainAll
等方法则根据删除的元素数增加modCount的值(removeAll和retainAll都是调用batchRemove实现,具体modCount
的修改算法还需研究)。

第一个代码片段中的操作方式1和操作方式2都是采用了迭代器的方式。当使用
iterator方法得到Iterator对象(或者使用listIterator获得ListItr对象),其实是返回了一个Iterator接口的实
现类ArrayList$Itr(继承自AbstractList$Itr),该类为ArrayList的一内部类,该类中有一个
expectedModCount字段,当调用ArrayList$Itr的next方法时,会先检查modCount的值是否等于
expectedModCount的值(其实在调用next,
remove, previous, set,
add等方法时都会检查),不相等时就会抛出java.util.ConcurrentModificationException异常。

为什么会抛出该异常呢?从代码可以看到调用了6次add方法,这时modCount的
值也就为6,当当使用iterator方法得到Iterator对象时把modCount的值赋给了expectedModCount,开始时
expectedModCount与modCount是相等的,当迭代到第二个元素(index=1)“string2”时,因为if条件为true,于
是又调用了remove方法,调用remove方法时modCount值又加1,此时modCount的值为7了,而expectedModCount的
值并没有改变,当再次调用ArrayList$Itr的next方法时检测到modeCount与expectedModCount不相等了,于是抛出异
常。

当把if语句写成if(s.equals("string5"))时又没有抛出该异
常,这又是为什么呢?ArrayList$Itr中还有一个名为cursor的字段用来指向迭代时要操作的元素索引,初始值为0,每调用一次next方法
该字段值加1,注意是先从集合中取出了元素再加1的。当判断"string5"时,注意是倒数第二个元素,这些cursor的值为5,移除掉元
素"string5"时,List的size为5,当调用ArrayList$Itr的hasNext方法判断有无下一个元素时,判断的依据为
cursor的值与size是否相等,不相等则还有下一个元素,而此时两者值刚好相等,也就没有往下执行next方法了,也就没有抛出异常,因此删掉倒数
第二个元素时不会抛异常的异常。

解决方案有四种,直接看第一段代码即可。

时间: 2024-09-29 04:19:38

java.util.ConcurrentModificationException异常分析的相关文章

android细节之java.util.ConcurrentModificationException异常

今天在做android项目的时候,遇到了这个异常,好吧,其实平常遇到最多的异常是IllegalstateException,都是跟我们硬件相连的SDK抛出来的,把我折磨的欲生欲死啊.扯远了.说回到今天的这个异常,java.util.ConcurrentModificationException异常,一开始我愣了一下,貌似从来没遇到过这个,然后果断百度大神,这才发现: 原因是你遍历该集合时,对该集合进行了删除元素的操作导致的,如果你有删除元素的必要,建议赋值到另一个集合,然后对他进行删除操作. 偶

java.util.ConcurrentModificationException异常原因及解决方法

在java语言中,ArrayList是一个很常用的类,在编程中经常要对ArrayList进行删除操作,在使用remove方法对ArrayList进行删除操作时,报java.util.ConcurrentModificationException异常,下面探讨一下该异常的原因以及解决办法. 1 import java.util.ArrayList; 2 import java.util.List; 3 4 public class Test { 5 6 public static void mai

java集合--java.util.ConcurrentModificationException异常

ConcurrentModificationException 异常:并发修改异常,当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常.一个线程对collection集合迭代,另一个线程对Collection进行修改的时候, 就会出现上面的异常. 下面看一下代码: package cn.itcast.p4.list.demo; import java.util.ArrayList; import java.util.Iterator; import java.util.List; pub

java.util.ConcurrentModificationException 异常解决办法及原理

最近在修程序的bug,发现后台抛出以下异常: Exception in thread "main" java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793) at java.util.HashMap$KeyIterator.next(HashMap.java:828) at com.keyman.demo.test.ClearResult

Java处理java.util.ConcurrentModificationException异常

代码: public static void reduce(HashMap<String, Integer> hashMap, final Integer count) { Iterator<String> iter = hashMap.keySet().iterator(); String key; while(iter.hasNext()) { key = iter.next(); if (!hashMap.get(key).equals(count)) { hashMap.r

Exception in thread &quot;Thread-1&quot; java.util.ConcurrentModificationException 异常原因和解决方法

注:参考博客:https://www.cnblogs.com/dolphin0520/p/3933551.html1.单线程环境下的异常重现 public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<Integer>(); list.add(2); Iterator<Integer> iterator = list.iterator(); while(iterator

【转】Java ConcurrentModificationException 异常分析与解决方案--还不错

原文网址:http://www.2cto.com/kf/201403/286536.html 一.单线程 1. 异常情况举例 只要抛出出现异常,可以肯定的是代码一定有错误的地方.先来看看都有哪些情况会出现ConcurrentModificationException异常,下面以ArrayList remove 操作进行举例: 使用的数据集合: ? 1 2 3 4 5 6 7 List<string> myList = new ArrayList<string>(); myList.

list删除操作 java.util.ConcurrentModificationException

首先大家先看一段代码: public static void main(String[] args) { List<String> listStr = new ArrayList<String>();      listStr.add("1");      listStr.add("2");      listStr.add("3");      listStr.add("4");      listS

java.util.ConcurrentModificationException故障分析

问题描述账号打通上线后发现偶尔会出现java.lang.RuntimeException: java.util.ConcurrentModificationException异常,这个是在生成请求签名的时候发生的问题,经分析应该是阿里云网关比较老版本的方法有bug.这个会导致线上用户突然掉线,体验太差. 出现该Exception的根本原因在对Vector.ArrayList在迭代的时候如果同时对其进行修改就会抛出java.util.ConcurrentModificationException异