fast-fail和fast-safe机制

前言

  在之前的JDK8 ArrayList源码解析P1中提到过,在对ArrayList进行遍历过程中,如果对集合进行结构性修改(增,删),会导致快速失败(fast-fial) 。那什么是fast-fail,什么又是safe-fail呢?

fast-fail(快速失败)

  在使用for循环遍历一个集合时,如果此时改变了集合的结构,如增、删,则会抛出ConcurrentModificationException异常,这个就称之为fast-fail

  原因:

  • 集合类中有一个modCount变量,在向集合中增加或者删除元素时都会修改这个变量的值;
  • 每当迭代器使用hasNext()/next()遍历下一个元素之前都会检测modCount变量和expectedModCount值是否相等;
  • 如果相等的话就返回遍历,否则抛出异常,终止遍历。

  具体代码如下:

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

  需要注意的是,在序号1处,异常的抛出条件时检测到modCount != expectedmodCount 这个条件。如果集合发生变化时修改modCount值, 刚好有设置为了expectedmodCount值, 则异常不会抛出。比如删除了数据,再添加一条数据。所以不能依赖于这个异常是否抛出而进行并发操作的编程, 这个异常只建议检测并发修改的bug。

  java.util包下的集合类都是快速失败机制的, 不能在多线程下发生并发修改(迭代过程中被修改)。

fail-safe ( 安全失败 )

  采用safe-fail安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先copy原有集合内容,在拷贝的集合上进行遍历。

  原理:

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

  缺点:

  基于拷贝内容的优点是避免了ConcurrentModificationException,但同样地, 迭代器并不能访问到修改后的内容。 (简单来说就是, 迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的)。

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

总结

  • java.util包下的集合类都是快速失败机制的, 不能在多线程下发生并发修改(迭代过程中被修改);
  • java.util.concurrent包下的容器都是安全失败的,可以在多线程下并发使用,并发修改;
posted @ 2020-07-26 16:03  凡尘多遗梦  阅读(816)  评论(0编辑  收藏  举报