集合中的快速失败(fail-fast)与安全失败(fail-safe)

什么是fail-fast(快速失败)

在用迭代器遍历集合时,当集合的结构被修改,会抛出ConcurrentModificationException异常。java

java.util包下的集合类都是快速失败的, 常见的的使用fail-fast方式遍历的容器有HashMapArrayList等。安全

fail-fast(快速失败)

  • 什么状况会出现fail-fast(快速失败)?

    1.单线程环境
    集合在遍历的过程当中,若是要对集合进行增删操做,没有调用迭代器的方法,而是用的集合自身的方法,则可能会产生fail-fast(快速失败)
    2.多线程环境下
    当一个线程在遍历某个集合的过程当中,另外一个线程对集合的结构进行了修改,则可能产生fail-fast(快速失败)

    多线程

  • 具体效果咱们看以下代码:spa

    ArrayList arrayList = new ArrayList();
           arrayList.add("a");
           arrayList.add("c");
           arrayList.add("d");
    
           Iterator iterator = arrayList.iterator();
           while (iterator.hasNext()) {
               System.out.println(iterator.next());
               arrayList.add("e"); ①
               System.out.println("此时 arrayList 长度为" + arrayList.size());
           }

    执行后的效果以下图:

    线程

    image.png

    当迭代器在遍历时,不对arrayList进行修改,也就是删除①时。

    image.png3d

    当迭代器在遍历时,对arrayList进行修改,也就保留①处代码。就会抛出 ConcurrentModificationException 异常

    code

  • 为何在用迭代器遍历时,修改集合就会抛异常?
  1. 缘由是迭代器在遍历时直接访问集合中的内容,而且在遍历过程当中使用一个modCount变量。集合在被遍历期间若是内容发生变化,就会改变modCount的值。
  2. 每当迭代器使用hashNext()/next()遍历下一个元素以前,都会检测modCount变量是否为 expectedModCount值,是的话就返回遍历;不然抛出异常,终止遍历。

什么是安全失败(fail-safe)

采用安全失败的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历,因此对原集合的修改并不会被迭代器检测到对象

因为迭代时是对原集合的拷贝进行遍历,因此在遍历过程当中对原集合所做的修改并不能被迭代器检测到,因此不会抛 ConcurrentModificationException异常。blog

java.util.concurrent包下面的全部的类都是安全失败的,如ConcurrentHashMap, CopyOnWriteArrayListhash

  • 具体效果咱们看以下代码:

    ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap();
           concurrentHashMap.put("a", 1);
           concurrentHashMap.put("b", 2);
           concurrentHashMap.put("c", 3);
    
           Set set = concurrentHashMap.entrySet();
           Iterator iterator = set.iterator();
    
           while (iterator.hasNext()) {
               System.out.println(iterator.next());
               concurrentHashMap.put("d", 4); ②
           }
           System.out.println("结束");
       }

    执行后的效果以下图:

    image.png

    当迭代器在遍历时,对concurrentHashMap进行修改时,也就②。并未抛出任何异常

  • 为何在用迭代器遍历时,修改集合不会抛异常?

    因为迭代时是对原集合的拷贝进行遍历,内部都是保存了该集合对象的一个快照副本,而且没有modCount等数值作检查。

    总结

    fail-safe(安全失败)容许在遍历的过程当中对容器中的数据进行修改,而fail-fast(快速失败)则不容许。

相关文章
相关标签/搜索