一:什么时候出现?

  当我们用迭代器循环list的时候,在其中用list的方法新增/删除元素,就会出现这个错误。

package com.sinitek.aml;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; public class ConcurrentModificationExceptionDemo { public static void main(String[] args) { List<String> arrayList = new ArrayList<>(); arrayList.add("a");
arrayList.add("b");
arrayList.add("c"); Iterator<String> iterator = arrayList.iterator();
while(iterator.hasNext()){
String tmp = iterator.next();
if ("a".equals(tmp)){
arrayList.remove(tmp); }
} }
}

二:为什么会出这个错误? 

 如上图所示,运行代码就会有这个错误,我们看一下堆栈。

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.sinitek.aml.ConcurrentModificationExceptionDemo.main(ConcurrentModificationExceptionDemo.java:22)

  

  可以看到,是在ArrayList里面的Itr内部类里的checkForModification()方法里,抛出的这个异常,我们看下源码。

    final void checkForComodification() {
//如果修改的次数 和 预期修改的次数不一致,就会报错
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}

  可以看到,上面两个关键的值是:modCount和expectModCount,其中的modCount来自ArrayList的父类,AbstractList,有兴趣的可以看下上面的翻译,也说明了这个字段的作用,正是为了fail-fast(快速失败)设计的。

  该值会在new出对象的时候,初始化为0;并且在每次的修改/删除操作时自增。

  

//arrayList的add方法,会先调用一次检查容量是否够的操作,我们可以看到,jdk专门在里面加了注释,会 Increments modCount, 增加modCount的值
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
} //确保新增元素容量大小够用的方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++; //首先先自增一次 // overflow-conscious code
if (minCapacity - elementData.length > 0) //判断最小需要的容量是否大于数组的长度(这里有个疑问,为什么一定相减然后判断是否>0,直接比较会有溢出的问题需要考虑么)
grow(minCapacity); //扩容
} //删除方法,也会有modCount++的操作
public E remove(int index) {
rangeCheck(index); modCount++;
E oldValue = elementData(index); int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work return oldValue;
}

  

那我们看一下,expectedModCount时怎么变化的。

  在ArrayList的内部类Itr(这个命名是不是有点随意了)里面,会看到expectedModCount是作为成员变量一起初始化的,所以我们在最开始的时候,调用list.iterator()方法的时候,就会初始化expectedModCount,将他变成和modCount一样的值。所以问题出在:

    1. new ArrayList,新增后,modCount会有一个初始值0。

    2. 调用iterator()方法后,会把expectedModCount的值初始化为modCount的值,此时,这两个值是一致的。

    3. 如果在循环里,使用list对象新增/删除对象(本文最上面所作的操作),modCount就会自增变化,而此时,expectedModCount却不会改变。

    4. 调用iterator里面的next() -> checkForComodification(),判断modCount==expectModCount的时候,就会发现不一致,从而抛出这个异常。

三:怎么解决。

  有两种方法。

    • 使用Itr类中的remove方法移除,但是如果用的是从Iterable继承的iterator返回的Iterator是没有add()方法的接口的,如果需要add(),那么需要调用list专有的迭代器listInterator()方法返回的ListIterator().
    • 不适用迭代器,使用原生的循环方式,这样就可以避免这个异常。(增强for只是一个语法糖(Syntactic Sugar)本质上还是使用了迭代器,也会有这个异常)。

四:为什么要定义这个异常?

    凡事多问一个为什么,jdk为什么要怎么设计,很简单,其实从命名上就能看出来,当我们在循环的时候,无论是我们在迭代循环的时候(目前我们就是这种情况),或者是其他的线程修改了这个(这个应该是jdk主要想解决的),都会出现数据不一致的情况,所以需要这个异常,让你fail-fast,早点异常早点再次尝试等。

  Q:为什么不在add()方法的时候,同时修改expectModCount,这样就他们一致了不就行了?

  A:add()方法在调用的时候,有可能没有这个Itr这个对象,而且单单修改值是没有用的,需要把对应的迭代器内部保存数据的值也对应的修改,只修改expectModCount有点类似掩耳盗铃。

  Q:那jdk可以新建一个重载方法,让itr对象当作参数,把他传进来,修改对应的保存数据的数组和对应的expectModCount,这样就能保证两个都一致了把。

  A:确实,这种方法对于上文这种fail-fast确实适用,但是还有一种情况就是:可能修改list的线程和迭代的线程都不是同一个,所以根本就没有itr这个对象,也就无法修改这个值了。

ArrayList中的ConcurrentModificationException,并发修改异常,fail-fast机制。的更多相关文章

  1. ConcurrentModificationException并发修改异常

    //创建集合对象 Collection c = new ArrayList(); c.add("hello"); c.add("world"); c.add(& ...

  2. 集合并发修改异常-foreach的时候不可修改值

    直接上代码: 无意间发现的://这个方法本身是为后面的集合去掉前面集合的重复数据一直报错,并发修改异常,仔细看mainList正在迭代循环,然后我进行了remove操作,这个时候就会报这个错.故:总结 ...

  3. 29.2 Iterator 迭代器ConcurrentModificationException:并发修改异常处理

    /** Iterator:迭代器* * 需求:判断集合中是否包含元素java,如果有则添加元素android * Exception in thread "main" java.u ...

  4. 【Java笔记】以并发修改异常为例总结的出错解决办法

    先来看出错代码: /*需求: 遍历已有集合 如果在集合中发现存在字符串元素"world" 则在"world"后添加元素"javaee" */ ...

  5. List集合遍历时修改元素出现并发修改异常总结

    什么是并发修改异常: 当我们在遍历实现了collection接口与iterator接口的集合时(List.Set.Map), 我们可以通过遍历索引也可以通过迭代器进行遍历.在我们使用迭代器进行遍历集合 ...

  6. 集合框架之——迭代器并发修改异常ConcurrentModificationException

    问题: 我有一个集合,如下,请问,我想判断里面有没有"world"这个元素,如果有,我就添加一个"javaee"元素,请写代码实现. 使用普通迭代器出现的异常: ...

  7. ArrayList在foreach删除倒数第二个元素不抛并发修改异常的问题

    平时我们使用ArrayList比较多,但是我们是否知道ArrayList在进行foreach的时候不能直接通过list的add或者move方法进行删除呢, 原因就是在我们进行foreach遍历的时候, ...

  8. ConcurrentModificationException(并发修改异常)的解决

    [异常解释] ConcurrentModificationException:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常.[产生的原因] 迭代器是依赖于集合而存在的,在判断成功后,集合 ...

  9. 理解和解决Java并发修改异常ConcurrentModificationException(转载)

    原文地址:https://www.jianshu.com/p/f3f6b12330c1 理解和解决Java并发修改异常ConcurrentModificationException 不知读者在Java ...

  10. 大杂烩 -- Iterator 并发修改异常ConcurrentModificationException

    基础大杂烩 -- 目录 大杂烩 -- Java中Iterator的fast-fail分析 大杂烩 -- Iterator 和 Iterable 区别和联系 问题: 在集合中,判断里面有没有" ...

随机推荐

  1. C++ 左值引用与一级指针

    将**左值引用**用于**一级指针**时,有以下几种用法: ```c++ //方式一:引用一级指针,常规用法 int a = 5; int * pa = &a; int * &rpa ...

  2. 几篇关于MySQL数据同步到Elasticsearch的文章---第四篇:使用go-mysql-elasticsearch同步mysql数据库信息到ElasticSearch

    文章转载自: https://www.cnblogs.com/dalaoyang/p/11018541.html 1.go-mysql-elasticsearch简介 go-mysql-elastic ...

  3. flask中验证用户登录的装饰器

    from flask import Flask,render_template,redirect,request,session from functools import wraps app = F ...

  4. APICloud可视化编程(二)

    上一期我们为大家讲解低代码在国内市场的当前现状以及APICloud可视化开发工具的组件类型和特点.相信大家对可视化编程有了一个整体的了解,那么今天带大家更进一步的使用组件. 下载开发工具 登录APIC ...

  5. Java(15)Object类

    前言 Object类是Java中所有类的始祖,在Java中每个类都扩展了Object.如果没有明确地指出超类,Object就被认为是这个类的超类.由于在Java中每个类都是由Object类扩展而来的, ...

  6. C++面向对象编程之reference

    1.声明 reference 一定要有初值,指针可以不用设初值 2. int& r = x; 表示 r 代表 x, r 用起来就是 x ,而且 reference 设完初值后再也不能代表其他变 ...

  7. 手写自定义springboot-starter,感受框架的魅力和原理

    一.前言 Springboot的自动配置原理,面试中经常问到,一直看也记不住,不如手写一个starter,加深一下记忆. 看了之后发现大部分的starter都是这个原理,实践才会记忆深刻. 核心思想: ...

  8. SpringBoot框架SpEL表达式注入漏洞复现与原理分析

    前言 这是2016年的一个洞,利用条件是至少知道一个触发 springboot 默认错误页面的接口及参数名. 影响版本:1.1.0-1.1.12 1.2.0-1.2.7 1.3.0 修复方案:升级版本 ...

  9. NLP之基于词嵌入(WordVec)的嵌入矩阵生成并可视化

    词嵌入 @ 目录 词嵌入 1.理论 1.1 为什么使用词嵌入? 1.2 词嵌入的类比推理 1.3 学习词嵌入 1.4 Word2Vec & Skip-Gram(跳字模型) 1.5 分级& ...

  10. 使用doctest代码测试和Sphinx自动生成文档

    python代码测试并自动生成文档 Tips:两大工具:doctest--单元测试.Sphinx--自动生成文档 1.doctest doctest是python自带的一个模块.doctest有两种使 ...