资讯专栏INFORMATION COLUMN

fail-fast与fail-safe在Java集合中的应用

Drummor / 3452人阅读

摘要:与在迭代器中的设计在中,最典型的与就是关于迭代器的设计。缺点是,迭代器不能正确及时的反应集合中的内容,而且一定程度上也增加了内存的消耗。

fail-fast与fail-safe简介

如果一个系统,当有异常或者错误发生时就立即中断执行,这种设计称之为fail-fast。相反如果我们的系统可以在某种异常或者错误发生时继续执行,不会被中断,这种设计称之为fail-safe

fail-fast与fail-safe在Java迭代器中的设计

在Java中,最典型的fail-fast与fail-safe就是关于迭代器的设计。通常情况下,那些线程不安全的集合类产生的迭代器都是fail-fast的,而线程安全的集合类产生的迭代器是fail-safe的。fail-fast的迭代器会在迭代过程中,如果你修改了集合类里的内容,则会抛出ConcurrentModificationException异常。fail-safe的迭代器则可以在迭代过程中任意修改集合类的内容,不会有异常抛出。

Java的fail-fast迭代器

前面说过,线程安全的集合类产生的迭代器是基于fail-fast设计的,例如ArrayList。这种迭代器迭代过程中是直接访问原数据信息的,所以当原集合内容修改了后,迭代器不能保证正确的迭代过程。代码示例如下:

    public static void failFast() {

        List list = new ArrayList<>();

        list.add("item-1");
        list.add("item-2");
        list.add("item-3");
        list.add("item-4");

        Iterator it = list.iterator();

        while (it.hasNext()) {
            String item = it.next();
            System.out.println(item);
            list.add("itme-5"); // 下次迭代时会抛出ConcurrentModificationException异常
        }

    }

通过分析ArrayList源码可以看出,当对ArrayList做添加或者删除元素的操作时,都会修改modCount这个变量,而ArrayList的迭代器每次迭代的时候,又都回去检查当前modCount和迭代器产生时的expectedModCount变量是否相等,如果不等就会抛出ConcurrentModificationException异常。

protected transient int modCount = 0;

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // 上面那个方法调用后会修改modCount
    ....
}


// ArrayList的迭代器

private class Itr implements Iterator {

    public E next() {
        checkForComodification();
        ...
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
    
    ...
}
Java的fail-safe迭代器

对于那些线程安全的集合类,在调用iterator方法产生迭代器的时候,会将当前集合的素有元素都做一个快照,即复制一份副本。每次迭代的时候都是访问这个快照内的元素,而不是原集合的元素。代码示例如下:

    public static void failSafe() {

        List list = new CopyOnWriteArrayList<>();

        list.add("item-1");
        list.add("item-2");
        list.add("item-3");
        list.add("item-4");

        Iterator it = list.iterator();

        while (it.hasNext()) {
            String item = it.next();
            System.out.println(item);
            list.add("itme-5");
        }

        System.out.println(list.size()); // 会打印出来8,迭代四次,四个新元素插入到了集合中。
    }

这种设计的好处是保证了在多线程操纵同一个集合的时候,不会因为某个线程修改了集合,而影响其他正在迭代访问集合的线程。缺点是,迭代器不能正确及时的反应集合中的内容,而且一定程度上也增加了内存的消耗。

迭代器小提示

如果用Java的for loop来访问集合,原理上还是用迭代器的方式,所以下面的代码同样会抛出ConcurrentModificationException异常。

        List list = new ArrayList<>();

        list.add("item-1");
        list.add("item-2");
        list.add("item-3");
        list.add("item-4");
        
        for (String item : list) {
            System.err.println(item);
            list.add("itme-5");
        }

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69187.html

相关文章

  • 快速失败(fail-fast)安全失败(fail-safe)

    摘要:注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出。 fail-fast与fail-safe 在Collection集合的各个类中,有线程安全和线程不安全这2大类的版本。 对于线程不安全的类,并发情况下可能会出现fail-fast情况;而线程安全的类,可能出现fail-safe的情况。 一、并发修改 当一...

    imtianx 评论0 收藏0
  • fail-fastfail-safe

    摘要:一什么是机制在用迭代器遍历集合时当集合的结构被修改会抛出异常二什么情况下集合的结构会被修改单线程环境集合在遍历的过程中如果要对集合进行增删操作没有调用迭代器的方法而是用的集合自身的方法则可能会产生事件多线程环境下当一个线程在遍历某个集合 一.什么是fail-fast机制? 在用迭代器遍历集合时,当集合的结构被修改,会抛出ConcurrentModificationException异常...

    魏明 评论0 收藏0
  • 带你了解集合世界的fail-fast机制 和 CopyOnWriteArrayList 源码详解

    摘要:体现的就是适配器模式。数组对象集合世界中的机制机制集合世界中比较常见的错误检测机制,防止在对集合进行遍历过程当中,出现意料之外的修改,会通过异常暴力的反应出来。而在增强循环中,集合遍历是通过进行的。 前言 学习情况记录 时间:week 2 SMART子目标 :Java 容器 记录在学习Java容器 知识点中,关于List的重点知识点。 知识点概览: 容器中的设计模式 从Array...

    young.li 评论0 收藏0
  • 面试官:说说快速失败和安全失败是什么

    摘要:我们都接触这些集合类,这些在包的集合类就都是快速失败的而包下的类都是安全失败,比如。安全失败明白了什么是快速失败之后,安全失败也是非常好理解的。最后说明一下,快速失败和安全失败是对迭代器而言的。 什么是快速失败(fail-fast)和安全失败(fail-safe)?它们又和什么内容有关系。以上两点就是这篇文章的内容,废话不多话,正文请慢用。 我们都接触 HashMap、ArrayLis...

    calx 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<