fail-safe fail-fast知多少

简介: fail-safe fail-fast知多少

目录



fail-safe fail-fast知多少


简介


我们在使用集合类的时候,通常会需要去遍历集合中的元素,并在遍历中对其中的元素进行处理。这时候我们就要用到Iterator,经常写程序的朋友应该都知道,在Iterator遍历的过程中,是不能够修改集合数据的,否则就会抛出ConcurrentModificationException。

因为ConcurrentModificationException的存在,就把Iterator分成了两类,Fail-fast和Fail-safe。


Fail-fast Iterator


Fail-fast看名字就知道它的意思是失败的非常快。就是说如果在遍历的过程中修改了集合的结构,则就会立刻报错。


Fail-fast通常在下面两种情况下抛出ConcurrentModificationException:


  1. 单线程的环境中

如果在单线程的环境中,iterator创建之后,如果不是通过iterator自身的remove方法,而是通过调用其他的方法修改了集合的结构,则会报错。


  1. 多线程的环境中

如果一个线程中创建了iterator,而在另外一个线程中修改了集合的结构,则会报错。

我们先看一个Fail-fast的例子:


Map<Integer,String> users = new HashMap<>();
        users.put(1, "jack");
        users.put(2, "alice");
        users.put(3, "jone");
        Iterator iterator1 = users.keySet().iterator();
        //not modify key, so no exception
        while (iterator1.hasNext())
        {
            log.info("{}",users.get(iterator1.next()));
            users.put(2, "mark");
        }


上面的例子中,我们构建了一个Map,然后遍历该map的key,在遍历过程中,我们修改了map的value。


运行发现,程序完美执行,并没有报任何异常。


这是因为我们遍历的是map的key,只要map的key没有被手动修改,就没有问题。

再看一个例子:


Map<Integer,String> users = new HashMap<>();
        users.put(1, "jack");
        users.put(2, "alice");
        users.put(3, "jone");
        Iterator iterator1 = users.keySet().iterator();
        Iterator iterator2 = users.keySet().iterator();
        //modify key,get exception
        while (iterator2.hasNext())
        {
            log.info("{}",users.get(iterator2.next()));
            users.put(4, "mark");
        }


上面的例子中,我们在遍历map的key的同时,对key进行了修改。这种情况下就会报错。


Fail-fast 的原理


为什么修改了集合的结构就会报异常呢?


我们以ArrayList为例,来讲解下Fail-fast 的原理。


在AbstractList中,定义了一个modCount变量:


protected transient int modCount = 0;


在遍历的过程中都会去调用checkForComodification()方法来对modCount进行检测:


public E next() {
            checkForComodification();
            try {
                int i = cursor;
                E next = get(i);
                lastRet = i;
                cursor = i + 1;
                return next;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException();
            }
        }


如果检测的结果不是所预期的,就会报错:


final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }


在创建Iterator的时候会复制当前的modCount进行比较,而这个modCount在每次集合修改的时候都会进行变动,最终导致Iterator中的modCount和现有的modCount是不一致的。


public void set(E e) {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();
            try {
                AbstractList.this.set(lastRet, e);
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }


注意,Fail-fast并不保证所有的修改都会报错,我们不能够依赖ConcurrentModificationException来判断遍历中集合是否被修改。


Fail-safe Iterator


我们再来讲一下Fail-safe,Fail-safe的意思是在遍历的过程中,如果对集合进行修改是不会报错的。


Concurrent包下面的类型都是Fail-safe的。看一个ConcurrentHashMap的例子:


Map<Integer,String> users = new ConcurrentHashMap<>();
        users.put(1, "jack");
        users.put(2, "alice");
        users.put(3, "jone");
        Iterator iterator1 = users.keySet().iterator();
        //not modify key, so no exception
        while (iterator1.hasNext())
        {
            log.info("{}",users.get(iterator1.next()));
            users.put(2, "mark");
        }
        Iterator iterator2 = users.keySet().iterator();
        //modify key,get exception
        while (iterator2.hasNext())
        {
            log.info("{}",users.get(iterator2.next()));
            users.put(4, "mark");
        }


上面的例子完美执行,不会报错。


总结


Fail-fast 和 Fail-safe 是集合遍历的重要概念,希望大家能够掌握。


本文的例子 https://github.com/ddean2009/learn-java-streams

相关文章
微信分享报错 wxlog:Error:fail to load Keychain status:-25300 解决办法
微信分享报错 wxlog:Error:fail to load Keychain status:-25300 解决办法
1730 0
|
7月前
|
Java 容器
Iterator_fail-fast和Iterator_fail-safe~
Iterator_fail-fast和Iterator_fail-safe~
|
7月前
|
Java
什么是fail-fast
什么是fail-fast
28 0
|
7月前
|
监控 安全
故事会【Fail-safe和Fail-fast】
故事会【Fail-safe和Fail-fast】
|
9月前
|
安全 Java 容器
什么是fail-fast和fail-safe?
本章讲解了什么是fail-fast和fail-safe,以及如何解决
72 0
|
10月前
|
Java
【Java】从源码分析fail-fast和fail-safe是如何产生的
【Java】从源码分析fail-fast和fail-safe是如何产生的
47 0
|
开发工具 git
Fatal Not possible to fast-forward, aborting
git是一个很好用的版本管理工具,然而,有时候一些冲突还是让人很郁闷的。 遇到过两次merge报错,是在不同的情形下出现的。
1340 0
|
安全 Java 容器
简述快速失败(fail-fast)和安全失败(fail-safe)?
快速失败(fail-fast)和安全失败(fail-safe)
140 0
|
安全 Java
fail-safe 和 fail-fast 硬核解析,让你和面试官多聊十分钟!
简介 java.util 包下的 属于 fail-fast , 快速失败~ 😝 java.util.concurrent 包下的 属于 fail-safe ,安全失败~ 😝 简单来说 就是 fail-fast 在迭代时,如果发现 该集合数据 结构被改变 (modCount != expectedModCount),就会 抛出 ConcurrentModificationException 小伙伴们可以参考下 下面的代码简单实验一下~ 😋 fail-fast 实验代码 实验对象是 Hashtable,这里采用 jdk1.7 的写法 ~ 因为博主还在研究 下文
143 0