集合中并发异常ConcurrentModificationException的产生原因(fail-fast 事件)

并发异常ConcurrentModificationException

  • 首先要了解什么是并发异常?(从源码的角度分析查看)
  • 怎样才能产生并发异常?
  • 找出解决办法

一、什么是并发异常?

  1. 从字面意思来看: 同时发生的修改异常
    Concurrent——同时发生的,并存的
    Modification——修正,改正,变更
    Exception——异常

  2. JDK源码解读:
    集合中并发异常ConcurrentModificationException的产生原因(fail-fast 事件)_第1张图片
    上面一大串英文解释为:(来自搜狗英文转汉语翻译!)
    此异常可能由检测到并发的方法引发对象的修改,当这种修改是不允许的。
    例如,通常不允许一个线程修改集合,而另一个线程正在迭代它。总的来说,研究的结果:在这些情况下迭代是未定义的。一些迭代器实现(包括所有通用集合实现的实现)如果这种行为是检测到。这样做的迭代器称为“fair-fast快速失效迭代器”
    因为它们迅速而干净地失败了,而不是冒武断的风险,未来不确定时间的非确定性行为。请注意,此异常并不总是表示某个对象已被“不同”线程同时修改。如果单线程发出一系列违反,在对象收缩时,该对象可能引发此异常。
    例如,如果一个线程在集合被修改时直接修改集合,用快速失效迭代器迭代集合,迭代器会抛出这个异常。
    请注意,通常不能保证快速失效行为,快速故障操作尽最大努力抛出{ @代码并发修改异常}。因此,编写一个依赖于此的程序是错误的其正确性异常: < i > { @代码并发修改异常}应该只用于检测错误。
    看了一遍,还是很懵,那就从实例中分析一下由来吧!集合中并发异常ConcurrentModificationException的产生原因(fail-fast 事件)_第2张图片
    作者对ConcurrentModificationException的描述是
    常见于集合:Collection、Iterator、ListIterator、Vector、LinkedList、HashSet、HashTable、TreeMap、AbstractList。

二、怎么才能产生并发异常?

我以ArrayList为例,用Iterator迭代器进行遍历,在遍历中使用了一次对象的remove()方法。

public class ArrayListIterator219 {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<Integer>();
        arrayList.add(1);
        arrayList.add(4);
        arrayList.add(6);
        arrayList.add(2);

        //迭代器iterator遍历
        Iterator<Integer> iterator = arrayList.iterator();
        while (iterator.hasNext()) {
            Integer value = iterator.next();
        if (value == 4) {     //删除值为4的元素
                arrayList.remove(Integer.valueOf(4));
            }
            iterator.remove(); //迭代器提供的删除方法!
            System.out.print(value + " ");
        }

就抛出了如下图所示的并发异常问题,戳开源码看一看:
在这里插入图片描述

873行的异常:
集合中并发异常ConcurrentModificationException的产生原因(fail-fast 事件)_第3张图片
909行的异常:
集合中并发异常ConcurrentModificationException的产生原因(fail-fast 事件)_第4张图片
对上面两个图进行分析:
函数checkForComodification()检验集合是否被修改过:
908行出现了一个以前没有分析过的变量modCount??这是什么?
如果这个值不等于ExceptedModCount,就会抛出并发异常。在源码中找找modCount
在这里插入图片描述
发现在定义属性的时候,有一个被保护的变量modCount。它的注释为↓↓↓
大概意思是:此列表在结构上被结构修改的次数,结构修改是那些改变列表大小的修改,或者以某种方式干扰列表,以至于进程中的迭代可能会产生不正确的结果。
结构性修改又是什么?是指add()、remove()、clear()、addAll()等增、删集合元素的动作。每当执行一次,modCount都会+1。来记录当前集合被操作的次数
集合中并发异常ConcurrentModificationException的产生原因(fail-fast 事件)_第5张图片
873行异常中:
集合中并发异常ConcurrentModificationException的产生原因(fail-fast 事件)_第6张图片

所以总结起来引起并发异常的原因是:

集合本身的增删改会引起modCount版本号的数值改变,操作一次版本号+1。
在迭代器中会将modCount版本号进行备份,成为迭代器的版本号expectedModCount,如果再次对集合进行操作时,集合的版本号发生改变,迭代器的版本号并不发生改变,最后会判断两个版本号是否一致,不一致则会抛出并发异常。此时产生fair-fast 事件

三、解决办法

既然用迭代器会产生并发异常,那我们就不要用迭代器去遍历集合就不会产生异常了啊!此时for each也不行,因为它底层也是用迭代器来实现的,用for循环来遍历。

  for (int i = 0; i < arrayList.size(); i++) {
            System.out.print(arrayList.get(i) + " ");
        }
        System.out.println();*/

参考了一下这个博客的fail-safe对fail-fast进行改进

你可能感兴趣的:(Java集合框架)