代码块
Java
 
 
 
 
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.mashibing.tank.Test2.main(Test2.java:14)
 

ArrayList在迭代删除元素时,如果使用不当会发生concurrentModification 异常,常见的错误使用场景

代码块
Java
 
 
 
 
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
    Integer next = iterator.next();
        list.remove(next);
}
 
代码块
Java
 
 
 
 
for (Integer next:list
     ) {
    list.remove(next);
}
 

发生异常原因:

首先明确以上两种方式是等效的,原因:增强for在反编译后语句

代码块
Java
 
 
 
 
Iterator var4 = var1.iterator();
while(var4.hasNext()) {
    Integer var3 = (Integer)var4.next();
    var1.remove(var3);
}
 

然后我们去分析发生concurrentModification原因:

由于异常发生在 :java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909),我们先去看下此处代码

发现remove(intxOrObject) 方法为 ArrayList 内部类Itr的一个方法 ,当modCount != expectedModCount 时会抛出异常ConcurrentModificationException。

先来解释下概念:

modCount:为 成员变量 list 被修改的次数 ,每次add ,remove都会+1 (The number of times this list has been )

代码块
Java
 
 
 
 
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>{
      //The number of times this list has been
      protected transient int modCount = 0;
 

expectedModCount:为listIterator 方法 局部变量,表示对ArrayList修改次数的期望值,它的初始值为modCount

代码块
Java
 
 
 
 
 
public Iterator<E> iterator() {
    return listIterator();
}
public ListIterator<E> listIterator(final int index) {
    checkForComodification();
    rangeCheckForAdd(index);
    final int offset = this.offset;
    return new ListIterator<E>() {
        int cursor = index;
        int lastRet = -1;
        //注意这里
        int expectedModCount = ArrayList.this.modCount;
        .......
        }
        ....
      }
      .....
  }
 
代码块
Java
 
 
 
 
 
private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;
    Itr() {}
    public boolean hasNext() {
        return cursor != size;
    }
    @SuppressWarnings("unchecked")
    public E next() {
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }
}
 

这里假设原始list 长度为10(只做过新增,没有做过删除操作) 。

这时候最初 AbstractList.modCount =10 ;

Iterator.expectedModCount = modCount =10 ;

1.当第一次循环调用next时,无变动list ,所以expectedModCount = modCount =10 ,

2.当 arrayList.remove(indexOrObject)时,modCount++ 变为11 ,expectedModCount 依然为10 。

3.第二次调用next 时,Itr 会执行checkForComodification() ,也就是判断expectedModCount、 modCount 是否一致,如果不一致则抛出ConcurrentModificationException 。

正确姿势:

关注点:

1.增强for循环不要删除元素

2.使用Iterator<Integer> iterator = list.iterator(); 方式时,先获取元素,然后使用iterator.remove() 进行删除

代码块
Java
 
 
 
 
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext() ) {
    System.out.println(iterator.next());
    iterator.remove();
}
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
    list.remove(i);
}
 

为什么Iterator<Integer> iterator = list.iterator(); 方式时 用iterator remove 不会出现问题?看源码:

代码块
Java
 
 
 
 
private class Itr implements Iterator<E> {
    public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();
    try {
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
        //关注这里, Itr里将modCount 赋值给expectedModCount 
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
    }
 

为什么普通循环不会有ConcurrentModificationException ,看源码:

代码块
Java
 
 
 
 
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    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++;
        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
    }
}
 

List remove ConcurrentModificationException源码分析的更多相关文章

  1. 集合操作出现的ConcurrentModificationException(源码分析)

    摘要: 为了保证线程安全,在迭代器迭代的过程中,线程是不能对集合本身进行操作(修改,删除,增加)的,否则会抛出ConcurrentModificationException的异常. 示例: publi ...

  2. Java并发-ConcurrentModificationException原因源码分析与解决办法

    一.异常原因与异常源码分析 对集合(List.Set.Map)迭代时对其进行修改就会出现java.util.ConcurrentModificationException异常.这里以ArrayList ...

  3. HashMap与TreeMap源码分析

    1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...

  4. 【集合框架】JDK1.8源码分析之HashMap & LinkedHashMap迭代器(三)

    一.前言 在遍历HashMap与LinkedHashMap时,我们通常都会使用到迭代器,而HashMap的迭代器与LinkedHashMap迭代器是如何工作的呢?下面我们来一起分析分析. 二.迭代器继 ...

  5. 【JUC】JDK1.8源码分析之CopyOnWriteArrayList(六)

    一.前言 由于Deque与Queue有很大的相似性,Deque为双端队列,队列头部和尾部都可以进行入队列和出队列的操作,所以不再介绍Deque,感兴趣的读者可以自行阅读源码,相信偶了Queue源码的分 ...

  6. 【JUC】JDK1.8源码分析之ConcurrentSkipListSet(八)

    一.前言 分析完了CopyOnWriteArraySet后,继续分析Set集合在JUC框架下的另一个集合,ConcurrentSkipListSet,ConcurrentSkipListSet一个基于 ...

  7. HashMap实现原理及源码分析

    哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...

  8. JDK Collection 源码分析(2)—— List

    JDK List源码分析 List接口定义了有序集合(序列).在Collection的基础上,增加了可以通过下标索引访问,以及线性查找等功能. 整体类结构 1.AbstractList   该类作为L ...

  9. [源码解析]HashMap和HashTable的区别(源码分析解读)

    前言: 又是一个大好的周末, 可惜今天起来有点晚, 扒开HashMap和HashTable, 看看他们到底有什么区别吧. 先来一段比较拗口的定义: Hashtable 的实例有两个参数影响其性能:初始 ...

随机推荐

  1. Java IO: FileInputStream

    原文链接 作者: Jakob Jenkov 译者: 李璟(jlee381344197@gmail.com) FileInputStream可以以字节流的形式读取文件内容.FileInputStream ...

  2. SQL语言分为四类,每类分别是?各包括什么?

    sqlserver(T_SQL):DDL—数据定义语言(CREATE,ALTER,DROP,DECLARE) DML—数据操纵语言(SELECT,DELETE,UPDATE,INSERT) DCL—数 ...

  3. VBA 读取加密的Excel文件(VBA 加密Excel)

    实验成功的: ExcelApp.Workbooks.Open(文件路径,,,'密码') 这里很坑,搜了别人的博客,下面这个方法试了N次,都没用... ExcelApp.Workbooks.Open(文 ...

  4. @RequestBody 参数为string正常改为对象时不报错但获取不到值

    @RequestBody 参数为string正常改为对象时不报错但获取不到值 试了好多办法都不行 最后 原因  jackson 包的版本号不匹配 2.9.0的不能封装进对象,可以运行且不报错但获取不到 ...

  5. 录音文件lame转换MP3相关配置

    文件下载整个功能完成了,那么对应的文件上传也跑不了.So~ Look here~ 业务需求是录制音频然后上传到七牛并且Android可以读. 与安卓沟通了一下统一了mp3格式,大小质量都不错.由于AV ...

  6. drbd配置

    DRBD就是网络RAID1,可以通过网络同步两个主机上的硬盘,drbd每次只允许对一个节点进行读写访问. 一.安装DRBD CentOS 6.x rpm -ivh http://www.elrepo. ...

  7. oracle监控参数

    Sar –u 检查CPU的繁忙程度列说明Usr用户模式下cpu运行所占的百分比Sys系统模式下cpu运行所占的百分比Wio因为有进程等待块I/O而使cpu处于闲置状态所占百分比IdleCpu为闲置状态 ...

  8. Redis 中的过期元素是如何被处理的?视频+图文版给你答案——面试突击 002 期

    本文以面试问题「Redis 中的过期元素是如何被处理的?」为切入点,用视频加图文的方式和大家聊聊 Redis 过期元素被处理的相关知识点. 涉及的知识点 过期删除策略有哪些? 这些过期策略有哪些优缺点 ...

  9. [面试专题]前端需要知道的web安全知识

    前端需要知道的web安全知识 标签(空格分隔): 未分类 安全 [Doc] Crypto (加密) [Doc] TLS/SSL [Doc] HTTPS [Point] XSS [Point] CSRF ...

  10. 【echarts3】 折线图我踩过的那些坑 (tooltip 设置,line 单个点/散点不显示问题)

    echarts 折线图小技巧 echarts 折线图功能丰富且官方文档详细易懂,上手比较容易,这篇文章将分享一些项目中踩过的坑,示例主要以多条曲线为主,对大家完成一些 曲线.tooltip 和 mar ...