工作中碰到个ConcurrentModificationException。代码如下:

List list = ...;
for(Iterator iter = list.iterator(); iter.hasNext();) {
    Object obj = iter.next();
    ...
    if(***) {
        list.remove(obj);
    }
}
在执行了remove方法之后,再去执行循环,iter.next()的时候,报java.util.ConcurrentModificationException(当然,如果remove的是最后一条,就不会再去执行next()操作了)

下面来看一下源码
public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove();
}

public interface Collection<E> extends Iterable<E> {
    ...
    Iterator<E> iterator();
    boolean add(E o);
    boolean remove(Object o);
    ...
}
这里有两个remove方法

接下来来看看AbstractList
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {  
//AbstractCollection和List都继承了Collection
    protected transient int modCount = 0;
    private class Itr implements Iterator<E> {  //内部类Itr
        int cursor = 0;
        int lastRet = -1;
        int expectedModCount = modCount;

public boolean hasNext() {
            return cursor != size();
        }

public E next() {
            checkForComodification();  //特别注意这个方法
            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;  //重新设置了expectedModCount的值,避免了ConcurrentModificationException的产生
            } catch(IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }

final void checkForComodification() {
            if (modCount != expectedModCount)  //当expectedModCount和modCount不相等时,就抛出ConcurrentModificationException
                throw new ConcurrentModificationException();
        }
    }    
}

remove(Object o)在ArrayList中实现如下:
public boolean remove(Object o) {
    if (o == null) {
            for (int index = 0; index < size; index++)
        if (elementData[index] == null) {
            fastRemove(index);
            return true;
        }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}
private void fastRemove(int index) {
    modCount++;  //只增加了modCount
    ....
}

所以,产生ConcurrentModificationException的原因就是:
执行remove(Object o)方法之后,modCount和expectedModCount不相等了。然后当代码执行到next()方法时,判断了checkForComodification(),发现两个数值不等,就抛出了该Exception。
要避免这个Exception,就应该使用remove()方法。

这里我们就不看add(Object o)方法了,也是同样的原因,但没有对应的add()方法。一般嘛,就另建一个List了

下面是网上的其他解释,更能从本质上解释原因:
Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。
所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。

好文:http://www.cnblogs.com/skywang12345/p/3308762.html

ConcurrentModificationException 详解的更多相关文章

  1. java.util.ConcurrentModificationException详解

    引用于http://blog.csdn.net/dabing69221/article/details/40065071 在使用set/map时,一个可爱的小bug:Java.util.Concurr ...

  2. 迭代器Iterator与ConcurrentModificationException详解

    背景:一直以来对迭代器的问题理解不是很透彻,特别是迭代器和异常ConcurrentModificationException之间的联系.通过debug,详细了解其底层的具体实现过程. 简介 Itera ...

  3. java的集合框架最全详解

    java的集合框架最全详解(图) 前言:数据结构对程序设计有着深远的影响,在面向过程的C语言中,数据库结构用struct来描述,而在面向对象的编程中,数据结构是用类来描述的,并且包含有对该数据结构操作 ...

  4. 转:Java HashMap实现详解

    Java HashMap实现详解 转:http://beyond99.blog.51cto.com/1469451/429789 1.    HashMap概述:    HashMap是基于哈希表的M ...

  5. 【转】java list用法示例详解

    转自:http://www.jb51.net/article/45660.htm java中可变数组的原理就是不断的创建新的数组,将原数组加到新的数组中,下文对java list用法做了详解. Lis ...

  6. Java SE之快速失败(Fast-Fail)与快速安全(Fast-Safe)的区别[集合与多线程/增强For](彻底详解)

    声明 特点:基于JDK源码进行分析. 研究费时费力,如需转载或摘要,请显著处注明出处,以尊重劳动研究成果:博客园 - https://www.cnblogs.com/johnnyzen/p/10547 ...

  7. Java集合详解8:Java的集合类细节精讲

    Java集合详解8:Java集合类细节精讲 今天我们来探索一下Java集合类中的一些技术细节.主要是对一些比较容易被遗漏和误解的知识点做一些讲解和补充.可能不全面,还请谅解. 本文参考:http:// ...

  8. Java集合详解3:Iterator,fail-fast机制与比较器

    Java集合详解3:Iterator,fail-fast机制与比较器 今天我们来探索一下LIterator,fail-fast机制与比较器的源码. 具体代码在我的GitHub中可以找到 https:/ ...

  9. java集合类之LinkedList详解

    一.LinkedList简介 由于LinkedList是一个实现了Deque的双端队列,所以LinkedList既可以当做Queue,又可以当做Stack,在将LinkedList当做Stack时,使 ...

随机推荐

  1. 安卓程序代写 网上程序代写[原]Android之Bluetooth编程

    ViewGroup 相关资料 : http://www.incoding.org/admin/archives/199.html http://bbs.csdn.net/topics/37014474 ...

  2. 自然语言交流系统 phxnet团队 创新实训 项目博客 (三)

    语音转文本部分是调用的科大讯飞的在线语音,它的激发方式是按键,通过按钮触发开启安卓设备的录音,此部分需要在源码中写入关于安卓权限的要求,来调用安卓的录音权限,当按钮被激发,则开始进入语音录制阶段,将麦 ...

  3. 【转】【CentOS】【Python】Centos7安装Python3的方法

    由于centos7原本就安装了Python2,而且这个Python2不能被删除,因为有很多系统命令,比如yum都要用到. [root@VM_105_217_centos Python-3.6.2]# ...

  4. 【WPF】鼠标拖拽功能DragOver和Drop

    在Winform里面实现拖入功能只要设置控件AllowDrop=true; 然后实现方法 //拖入 private void txtInputPath_DragOver(object sender, ...

  5. Android 8 wifi blakclist

    在连接wifi的时候,认证或者关联失败,有时会加入黑名单中.记录wpa_supplicant中blacklist的原理. 分析可以看到,如果是机器自己断开,是不会把AP加入黑名单的,只有AP侧出了问题 ...

  6. JDBC异常

    异常处理允许我们以受控的方式处理异常情况,而不是直接退出程序,例如程序定义的错误. 发生异常时可以抛出异常.术语“异常”表示当前的程序执行停止,并且被重定向到最近的适用的catch子句.如果没有适用的 ...

  7. JAVA并发:深入分析volatile

    Java volatile 汇编代码研究 JVM执行篇:使用HSDIS插件分析JVM代码执行细节 聊聊并发(一)——深入分析Volatile的实现原理 深入Java底层:内存屏障与JVM并发详解 深入 ...

  8. jQuery 文件上传插件:uploadify、swfupload

    jQuery 文件上传插件: uploadify.swfupload

  9. (https://www.ibm.com/developerworks/community/forums/html/topic?id=77777777-0000-0000-0000-000014550004)Topic: Caught java.io.CharConversionException. ERRORCODE=-4220, SQLSTATE=null

    270002WDPN                                            3 Posts                             0 people l ...

  10. JS jQuery json日期格式问题的办法

    原生JS:Date对象详细参考 Date对象:基于1970年1月1日(世界标准时间)起的毫秒数 本文参考MDN做的详细整理,方便大家参考MDN 构造函数: new Date(); 依据系统设置的当前时 ...