美文网首页
Java集合中的fail-fast机制

Java集合中的fail-fast机制

作者: 8813d76fee36 | 来源:发表于2018-04-26 14:29 被阅读27次

fail-fast简介

在java.util包下的集合类中,有一个成员变量modCount。

protected transient int modCount = 0;

这个变量的数值表示使集合发生结构变化的次数。
引起结构变化的操作指诸如影响集合大小(新增、删除元素)或干扰遍历结果的操作。

原理简述

当调用会改变集合结构的操作,如添加、删除元素时,会使变量modCount的数值增加1,以此来记录当前集合的结构被修改了多少次。迭代器被实例话的时候,其内部也会拷贝一份该数值,存入迭代器成员变量expectedModCount中。在迭代器操作集合(如netx()、remove() )之前会校验expectedModCount与集合中的modCount值是否一致,若不一致,则说明在遍历过程中,集合的结构发生了变化,此时迭代器会立即终止对集合的操作,抛出ConcurrentModificationException异常。

实现机制解析

我们以ArrayList为例,从源码入手看看这个机制是如何工作的。

  • 实例化一个新的ArrayList,并为它添加5个元素。
        List<Integer> list = new ArrayList<>();

        for (int i = 0; i < 5; i ++) {
            list.add(i);
        }

在add()方法中我们可以看到modCount累加的操作。

private void ensureExplicitCapacity(int minCapacity) {
        modCount++; //操作数自增一

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

由于我们使用循环依次添加了5个元素,也就是调用了5次add方法,因此结构应该发生了5次变化,此时modCount的数值应该为5。


modCount
  • 尝试触发fail-fast
    此时我们已经有了一个包含了5个元素的ArrayList,现在可以尝试触发fail-fast了。
    模拟一个需求:遍历该List,如果当前元素是偶数,则从删除集合内删除。
    很多初学者会想到如下实现:
        for (Integer num : list) {
            if (num % 2 == 0) {
                list.remove(num);
            }
        }

事实证明,该代码在执行时会抛出异常,而该异常正是ConcurrentModificationException异常。

  • 如何触发的fail-fast呢?
    for-each(又称增强for)与普通的for循环遍历不同,它实际上调用的是集合的迭代器,它的效率比普通for循环要高。

我们来看看上面这段代码发生了什么。

迭代器

如图可见,for-each调用了ArrayList的迭代器,此时迭代器的expectedModCount已经被modCount赋值为5。
当调用next()方法获取元素时,首选会调用checkForComodification()方法检查集合结构是否被修改。


checkForComodification

而检查的依据就是判断expectedModCount和modCount的值是否相等。


checkForComodification内部
由于现在是遍历操作,不会引起集合结构变化,所以modCount值没有改变,第一次遍历检查通过,成功拿到第一个元素0,并且由于0是偶数,因此它被成功删除。
0已经不见了
继续执行遍历,由于刚才执行了remove()方法,list的结构发生变化,modCount也随之自增,此时expectedCount已经和modCount不同了。
modCount发生改变

这时迭代器的checkForComodification方法检测到两个数值不一致,抛出了ConcurrentModificationException异常。


ConcurrentModificationException
  • 如何避免ConcurrentModificationException异常呢
    针对上面模拟的需求,我们应该使用迭代器遍历删除。
Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()) {
            if (iterator.next() % 2 == 0) {
                iterator.remove();
            }
        }
迭代器remove

此时没有触发ConcurrentModificationException异常是因为迭代器的remove()方法中,在删除元素后,将expectedModCount的值做了同步。
但在并发环境下解决这个问题还应该使用java.util.current包下的集合类。该包下的集合类在修改集合结构时没有在原集合直接修改,而是将数据复制到一个新数组内,在新数组内修改后再将值同步到原数组中。

下面是java.util.current.CopyOnWriteArrayList类的说明

 * A thread-safe variant of {@link java.util.ArrayList} in which all mutative
 * operations ({@code add}, {@code set}, and so on) are implemented by
 * making a fresh copy of the underlying array.

相关文章

  • fail-fast 与 fail-safe

    先说说什么是fail-fast fail-fast 机制是java集合(Collection)中的一种错误机制。在...

  • Fail-Fast和Fail-Safe机制

    1 Fail-Fast 1.1 fail-fast定义 fail-fast 机制是java集合(Collectio...

  • 集合的快速失败机制

    Java集合的快速失败机制 “fail-fast”? 答: 是java集合的一种错误检测机制,当多个线程对集合进行...

  • Fail-Fast 机制

    简介 fail-fast 机制是 java 集合(Collection)中的一种错误机制。 当多个线程对同一个集合...

  • Java集合框架中的快速失败(fail—fast)机制详解

      fail-fast机制,即快速失败机制,是java集合框架中的一种错误检测机制。在用迭代器遍历一个集合对象时,...

  • fail-fast机制

    1.概念 fail-fast 机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的...

  • Fail-Fast:Java集合错误检测机制

    一、简介fail-fast,即快速失败,Java集合的一种错误检测机制。当多个线程对集合(非fail-fast的集...

  • Java 集合

    fail-fast机制 在java集合类中,使用modCount来检查数组的状态.当在迭代集合的时候,(通常会实现...

  • fail-fast机制

    fail-fast机制是java集合中的一种错误机制。当使用迭代器迭代时,如果发现集合有修改,则快速失败做出响应,...

  • 集合知识

    目录 集合图 集合机制 快速失败 java.util包下面的所有的集合类都是快速失败fail-fast迭代器的,而...

网友评论

      本文标题:Java集合中的fail-fast机制

      本文链接:https://www.haomeiwen.com/subject/zjbplftx.html