• fail-fast和fail-safe


    Fail-fast 和 Fail-safe会涉及到Javaz中的一些术语。

    首先得先了解并发修改。

    1.什么是并发修改?

    当一个或多个线程正在遍历一个集合Collection,此时另一个线程修改了这个集合的内容(添加,删除或者修改)。

    这就是并发修改

    2.什么是 fail-fast 机制?

    fail-fast机制在遍历一个集合时,当集合结构被修改,会抛出Concurrent Modification Exception。

     结构上的更改指的是删除或者插入一个元素,这样会影响到map的结构

    fail-fast会在以下两种情况下抛出ConcurrentModificationException

    (1)单线程环境

    集合被创建后,在遍历它的过程中修改了结构。

    注意 迭代器自身的remove()方法会让expectModcount和modcount 相等,所以是不会抛出这个异常。

     1 public void remove()
     2      {
     3             if (lastRet < 0)
     4                 throw new IllegalStateException();
     5             checkForComodification();
     6             try {
     7                 ArrayList.this.remove(lastRet);
     8                 cursor = lastRet;
     9                 lastRet = -1;
    10                 expectedModCount = modCount;
    11             } catch (IndexOutOfBoundsException ex) {
    12                 throw new ConcurrentModificationException();
    13             }
    14      }

    (2)多线程环境

    当一个线程在遍历这个集合,而另一个线程对这个集合的结构进行了修改。 

    注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。

    3. fail-fast机制是如何检测的?

    迭代器在遍历过程中是直接访问内部数据的,因此内部的数据在遍历的过程中无法被修改。为了保证不被修改,迭代器内部维护了一个标记 “mode” ,当集合结构改变(添加删除或者修改),标记"mode"会被修改,而迭代器每次的hasNext()和next()方法都会检查该"mode"是否被改变,当检测到被修改时,抛出Concurrent Modification Exception

    。下面看看ArrayList迭代器部分的源码。

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片

            private class Itr implements Iterator<E> {    

    1. int cursor;    
    2. int lastRet = -1;    
    3. int expectedModCount = ArrayList.this.modCount;    
    4. public boolean hasNext() {    
    5. return (this.cursor != ArrayList.this.size);    
    6.         }    
    7. public E next() {    
    8.             checkForComodification();    
    9. /** 省略此处代码 */    
    10.         }    
    11. public void remove() {    
    12. if (this.lastRet < 0)    
    13. throw new IllegalStateException();    
    14.             checkForComodification();    
    15. /** 省略此处代码 */    
    16.         }    
    17. final void checkForComodification() {    
    18. if (ArrayList.this.modCount == this.expectedModCount)    
    19. return;    
    20. throw new ConcurrentModificationException();    
    21.         }    
    22.     }</span>  


    可以看到它的标记“mode”为 expectedModeCount

    4. fail-safe机制

    fail-safe任何对集合结构的修改都会在一个复制的集合上进行修改,因此不会抛出ConcurrentModificationException

    fail-safe机制有两个问题

    (1)需要复制集合,产生大量的无效对象,开销大

    (2)无法保证读取的数据是目前原始数据结构中的数据。

    5 fail-fast 和 fail-safe的例子

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <span style="font-size:14px;">import java.util.HashMap;    
    2. import java.util.Iterator;    
    3. import java.util.Map;    
    4.     
    5. public class FailFastExample    
    6. {    
    7.        
    8.     public static void main(String[] args)    
    9.     {    
    10.         Map<String,String> premiumPhone = new HashMap<String,String>();    
    11.         premiumPhone.put("Apple", "iPhone");    
    12.         premiumPhone.put("HTC", "HTC one");    
    13.         premiumPhone.put("Samsung","S5");    
    14.             
    15.         Iterator iterator = premiumPhone.keySet().iterator();    
    16.             
    17.         while (iterator.hasNext())    
    18.         {    
    19.             System.out.println(premiumPhone.get(iterator.next()));    
    20.             premiumPhone.put("Sony", "Xperia Z");    
    21.         }    
    22.             
    23.     }    
    24.         
    25. }  </span>  


    输出

    iPhone 
    Exception in thread "main" java.util.ConcurrentModificationException
            at java.util.HashMap$HashIterator.nextEntry(Unknown Source)
            at java.util.HashMap$KeyIterator.next(Unknown Source)
            at FailFastExample.main(FailFastExample.java:20)
     
    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <span style="font-size:14px;">import java.util.concurrent.ConcurrentHashMap;    
    2. import java.util.Iterator;    
    3.     
    4.     
    5. public class FailSafeExample    
    6. {    
    7.         
    8.         
    9.     public static void main(String[] args)    
    10.     {    
    11.         ConcurrentHashMap<String,String> premiumPhone =     
    12.                                new ConcurrentHashMap<String,String>();    
    13.         premiumPhone.put("Apple", "iPhone");    
    14.         premiumPhone.put("HTC", "HTC one");    
    15.         premiumPhone.put("Samsung","S5");    
    16.             
    17.         Iterator iterator = premiumPhone.keySet().iterator();    
    18.             
    19.         while (iterator.hasNext())    
    20.         {    
    21.             System.out.println(premiumPhone.get(iterator.next()));    
    22.             premiumPhone.put("Sony", "Xperia Z");    
    23.         }    
    24.             
    25.     }    
    26.         
    27. }  </span>  
    输出
    S5
    HTC one
    iPhone
     
    Iterator的安全失败是基于对底层集合做拷贝,因此,它不受源集合上修改的影响。java.util包下面的所有的集合类都是快速失败的,而java.util.concurrent包下面的所有的类都是安全失败的。快速失败的迭代器会抛出ConcurrentModificationException异常,而安全失败的迭代器永远不会抛出这样的异常.

    6. fail-fast和 fail-safe 的区别



     Fail Fast IteratorFail Safe Iterator
    Throw ConcurrentModification Exception Yes No
    Clone object No Yes
    Memory Overhead No Yes
    Examples HashMap,Vector,ArrayList,HashSet
    CopyOnWriteArrayList,
    ConcurrentHashMap

  • 相关阅读:
    亚马逊云IoT平台接入开发记录
    pip下载速度慢更换清华源试试
    gitlab回归上一次提交
    uos桌面壁纸存放路径
    python中json中的dump和dumps
    Python中的类中__dict__方法
    C++ | 数组反转的三种方法
    《C++Primer Plus》 | 复合类型
    pwn 中的函数 | 持续更新
    七月安恒DASCTF | 复现
  • 原文地址:https://www.cnblogs.com/fisherinbox/p/6659249.html
Copyright © 2020-2023  润新知