代码块
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. python使用geopandas和shapely处理shp文件

    一.环境搭建 所需库:geopandas (以及前置库)  doc:http://geopandas.org/ shapely(以及前置库)  doc: 二.数据预处理 1.将shp文件进行切片 2. ...

  2. 吴裕雄--天生自然python学习笔记:WEB数据抓取与分析

    Web 数据抓取技术具有非常巨大的应用需求及价值, 用 Python 在网页上收集数据,不仅抓取数据的操作简单, 而且其数据分析功能也十分强大. 通过 Python 的时lib 组件中的 urlpar ...

  3. haproxy笔记之二:HAProxy简介

    HAProxy提供高可用性.负载均衡以及基于TCP和HTTP应用的代理,支持虚拟主机,它是免费.快速并且可靠的一种解决方案.HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持 ...

  4. top100 paper

  5. 深入 Java 调试体系: 第 1 部分,JPDA 体系概览

    JPDA 概述 所有的程序员都会遇到 bug,对于运行态的错误,我们往往需要一些方法来观察和测试运行态中的环境.在 Java 程序中,最简单的,您是否尝试过使用 System.out.println( ...

  6. Handler机制中的消息队列

    --> 学习自蘑菇街大佬 Handler机制可以看成是一个消息阻塞队列,当有消息时立即处理消息,没有消息时则阻塞.在Android系统中APP启动后很快进入死循环,不断读取MessageQueu ...

  7. spring整合ehcache实现缓存

    Spring 提供了对缓存功能的抽象:即允许绑定不同的缓存解决方案(如Ehcache),但本身不直接提供缓存功能的实现.它支持注解方式使用缓存,非常方便. spring本身内置了对Cache的支持,之 ...

  8. Python Web 基础向(四) 浅谈数据层

    数据层一般会给人带来一些困扰,在于其定位不准确.聚合Model的工作也可以放在逻辑层做,但会导致逻辑层变重,经常出现大段晦涩代码.因此我的建议是保留Model聚合层,尽管会导致工作量的略微增加,但却可 ...

  9. 用 20 行 python 代码实现人脸识别!

    点击上方"Python编程与实战",选择"置顶公众号" 第一时间获取 Python 技术干货! 阅读文本大概需要 11分钟. 今天给大家介绍一个世界上最简洁的人 ...

  10. Hexo博客maupassant主题添加Google Adsense广告

    自从在 Github Page 落户以后,很长一段时间使用的是极简且有点艺术范儿的 fexo 主题,而不是大名鼎鼎的 next 主题.后来偶然发现了符合我审美的Hexo博客 maupassant 主题 ...