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

快速失败

在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出 Concurrent Modification Exception

迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变 modCount 的值。每当迭代器使用 hashNext()/next()遍历下一个元素之前,都会检测 modCount 变量是否为 expectedModCount 值,是的话就返回遍历;否则抛出异常,终止遍历。

这个机制我在之前的博客中ArrayList源码分析中有提到,也分析了迭代器中关于判断modCount是否为期望值的源码部分。

下面主要来看安全失败(fail-safe)

安全失败

采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。 这点和CopyOnWriteArrayList在修改上的的机制很一致。

原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发 Concurrent Modification Exception。

比如我们来看ConcurrentHashMap的迭代器:

static class Traverser<K,V> {
    Node<K,V>[] tab;        // current table; updated if resized
    Node<K,V> next;         // the next entry to use
    TableStack<K,V> stack, spare; // to save/restore on ForwardingNodes
    int index;              // index of bin to use next
    int baseIndex;          // current index of initial table
    int baseLimit;          // index bound for initial table
    final int baseSize;     // initial table size

    Traverser(Node<K,V>[] tab, int size, int index, int limit) {
        //在这里复制tab
        this.tab = tab;
        this.baseSize = size;
        this.baseIndex = this.index = index;
        this.baseLimit = limit;
        this.next = null;
    }

    //遍历逻辑略
}

缺点:基于拷贝内容的优点是避免了 Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。

场景:java.util.concurrent 包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

总结

快速失败和安全失败是对迭代器而言的。

快速失败:当在迭代一个集合的时候,如果有另外一个线程在修改这个集合,就会抛出 ConcurrentModification 异常,java.util 下的集合类都是快速失败。

安全失败:在迭代时候会在集合二层做一个拷贝,所以在修改集合上层元素不会影响下层。在 java.util.concurrent 下的并发集合类都是安全失败。

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/%e5%bf%ab%e9%80%9f%e5%a4%b1%e8%b4%a5fail-fast%e5%92%8c%e5%ae%89%e5%85%a8%e5%a4%b1%e8%b4%a5fail-safe/

(0)
彭晨涛彭晨涛管理者
上一篇 2020年2月23日
下一篇 2020年2月23日

相关推荐

  • JDK8u20字符串去重

    优点:节省大量内存 缺点:略微多占用cpu时间,新生代回收时间略微增加 -XX:+UseStringDeduplication String s1 = new String("he…

    Java 2020年1月15日
    0740
  • PriorityQueue源码分析

    总结 总结放前面防止太长不看: PriorityQueue是个最小堆,如果要改变排序顺序只能重写比较器传入构造方法。 内部元素要么实现Comparable接口,要么传入比较器进行比…

    Java 2020年2月10日
    0260
  • 日志门面介绍和JCL使用

    日志门面概述 之前有写过Java日志框架JUL和Log4j介绍,可以对比发现它们之间的使用还是有一些区别的,比如有一些类名是不同的、日志的输出级别也不完全一致。 那么实际上是增大了…

  • AQS及其应用ReentrantLock源码分析

    AQS原理 概述 全称是 AbstractQueuedSynchronizer(抽象同步队列),是阻塞式锁和相关的同步器工具的框架。 特点:+ 用 state 属性来表示资源的状态…

    2020年2月4日
    0120
  • 深入理解java虚拟机第三版读书笔记11

    以下是第十一章 后端编译与优化的内容 把Class文件转换成与本地基础设施(硬件指令集、操作系统)相关的二进制机器码可以视为整个编译过程的后端。 最近几年提前编译也开始兴起,我们在…

    2020年1月27日
    0290
  • JDK8新增高效原子累加器LongAdder源码分析

    很久以前写过CAS应用之JUC下的原子类,但是LongAdder这个类没有去看,只是给了一个其他博客的参考链接。今天就自己来分析一下。 AtomicLong的问题和LongAdde…

    2020年5月19日
    0740
  • Java网络套接字Socket编程

    Socket概述 Socket,译作网络套接字,是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。网络套接字与一个应用进程(ip和端口号)绑定。 Socket与Ser…

    Java 2020年2月5日
    0440
  • HashSet源码分析

    Set家族一览: HashSet简介 Set是Collection三大接口其中之一,意为集合,且元素不能重复。Set接口中的方法和Collection中的方法完全一致,只是起到一个…

    2019年12月2日
    0200
  • 对象的输入输出-Java序列化机制

    对象序列化和反序列化,在Java中体现为两种字节流: ObjectInputStream、ObjectOutputStream 序列化的概念 指堆内存中的java对象数据,通过某种…

    Java 2019年12月21日
    0170
  • NIO、BIO模型对比实现文件的复制

    NIO特点 使用Channel代替Stream,是双向的 使用Selector监控多条Channel 可以在一个线程里处理多个Channel I/O Channel和Buffer …

    Java 2020年2月7日
    0180

发表回复

登录后才能评论