代码块
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. 【转】mac os x配置adb命令的方法,苹果电脑设置adb命令的方法

    http://www.myexception.cn/operating-system/1636963.html 步骤如下: 1. 启动终端Terminal (如果当前用户文件夹下已有.bash_pro ...

  2. codeigniter注意点

    1. 数据的操作: insert 和update最好使用自带的db方法,1避免了过滤字符串和sql注入,2是数据量相对查询是非常小的. select则建议使用自己写的,这样便于优化. 2. 去掉ind ...

  3. mysql 优化配置和方面

    MySQL性能优化的参数简介 公司网站访问量越来越大,MySQL自然成为瓶颈,因此最近我一直在研究 MySQL 的优化,第一步自然想到的是 MySQL 系统参数的优化,作为一个访问量很大的网站(日20 ...

  4. Serializable 接口(序列化)

    目录 Serializable 接口(序列化) 前言 用途 如何实现 异常 serialVersionUID transient关键字 Serializable 接口(序列化) 前言 查看API文档时 ...

  5. redis下载安装及php配置redis

    下载及安装redis 1.首先去github网站上下载https://github.com/dmajkic/redis/downloads: 2.根据实际情况,将64bit的内容cp到自定义盘符目录, ...

  6. 关于Git的版本控制

    1.关于版本控制? 版本控制是一种记录文件或文件集随时间变化的系统,以便您以后可以调用特定版本,如果您是图形或Web设计人员并希望保留图像或布局的每个版本(您肯定希望这样),则使用版本控制系统(VCS ...

  7. SWUST OJ 爬不出去的水井(0333)

    爬不出去的水井(0333) Time limit(ms): 1000 Memory limit(kb): 65535 Submission: 1069 Accepted: 150 Descriptio ...

  8. 通过ELK快速搭建集中化日志平台

    ELK就是ElasticSearch + LogStash + Kibana 1.准备工作 ELK下载:https://www.elastic.co/downloads/ jdk version:1. ...

  9. sql问题处理

    批量杀死MySQL连接 select concat('KILL ',id,';') from information_schema.processlist where Info like 'selec ...

  10. Java8-19-lambda 重构代码

    通过本书的前七章,我们了解了Lambda和Stream API的强大威力. 你可能主要在新项目的代码中使用这些特性.如果你创建的是全新的Java项目,这是极好的时机,你可以轻装上阵,迅速地将新特性应用 ...