什么是Fail Fast和Fail Safe?

1. Fail Fast

a. 概念

在Java中可以使用Iterator来遍历集合对象。当一个线程使用iterator遍历集合元素时,如果集合的数据被修改了(增、删、改),支持Fail Fast的iterator会立刻抛出ConcurrentModificationExcepton异常。

  • 支持Fail Fast迭代器的集合对象有ArrayList, HashMap, Vector
b. 原理

fail-fast iterators使用了一个叫modCount的内部标识来标志集合数据是否被修改,每当集合的数据有修改,modCount就会被更新。每当iterator调用next()获取下一个集合元素时,都会检查modCount,如果发现在iterator被创建后,modCount值被修改了,就会抛出ConcurrentModificationExcepton异常。

// Java code to illustrate 
// Fail Fast Iterator in Java 
import java.util.HashMap; 
import java.util.Iterator; 
import java.util.Map; 
  
public class FailFastExample { 
    public static void main(String[] args) 
    { 
        Map<String, String> cityCode = new HashMap<String, String>(); 
        cityCode.put("Delhi", "India"); 
        cityCode.put("Moscow", "Russia"); 
        cityCode.put("New York", "USA"); 
  
        Iterator iterator = cityCode.keySet().iterator(); 
  
        while (iterator.hasNext()) { 
            System.out.println(cityCode.get(iterator.next())); 
  
            // adding an element to Map 
            // exception will be thrown on next call 
            // of next() method. 
            cityCode.put("Istanbul", "Turkey"); 
        } 
    } 
} 

OutPut

India
Exception in thread “main” java.util.ConcurrentModificationException
at java.util.HashMap H a s h I t e r a t o r . n e x t N o d e ( H a s h M a p . j a v a : 1442 ) a t j a v a . u t i l . H a s h M a p HashIterator.nextNode(HashMap.java:1442) at java.util.HashMap HashIterator.nextNode(HashMap.java:1442)atjava.util.HashMapKeyIterator.next(HashMap.java:1466)
at FailFastExample.main(FailFastExample.java:18)

c. 关注点
  1. 当使用iterator遍历集合时,如果集合元素被修改,就会抛出ConcurrentModificationException
  2. 迭代器使用原始集合遍历集合元素
  3. 这些迭代器不需要额外的内存空间
  4. ArrayList, HashMap, Vector返回的迭代器支持Fail Fast
d. 注意
  1. 迭代器的快速失败行为不能得到保证,因为一般来说,在存在非同步并发修改时不可能做出任何硬性保证(比如类似ABA的问题可能会发生)。快速失败迭代器尽最大努力抛出ConcurrentModificationException。因此,如果要编写一个依赖于这个异常来保证其正确性的程序,那将是错误的:迭代器的快速失败行为应该仅用于检测错误。
  2. 如果通过Iterator remove()方法删除元素,则不会抛出异常。但是,在通过特定集合的remove()方法进行删除时,将抛出ConcurrentModificationException。下面是示例:
// Java code to demonstrate remove 
// case in Fail-fast iterators 
  
import java.util.ArrayList; 
import java.util.Iterator; 
  
public class FailFastExample { 
    public static void main(String[] args) 
    { 
        ArrayList<Integer> al = new ArrayList<>(); 
        al.add(1); 
        al.add(2); 
        al.add(3); 
        al.add(4); 
        al.add(5); 
  
        Iterator<Integer> itr = al.iterator(); 
        while (itr.hasNext()) { 
            if (itr.next() == 2) { 
                // will not throw Exception 
                itr.remove(); 
            } 
        } 
  
        System.out.println(al); 
  
        itr = al.iterator(); 
        while (itr.hasNext()) { 
            if (itr.next() == 3) { 
                // will throw Exception on 
                // next call of next() method 
                al.remove(3); 
            } 
        } 
    } 
} 

OutPut

[1, 3, 4, 5]
Exception in thread “main” java.util.ConcurrentModificationException
at java.util.ArrayList I t r . c h e c k F o r C o m o d i f i c a t i o n ( A r r a y L i s t . j a v a : 901 ) a t j a v a . u t i l . A r r a y L i s t Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList Itr.checkForComodification(ArrayList.java:901)atjava.util.ArrayListItr.next(ArrayList.java:851)
at FailFastExample.main(FailFastExample.java:28)

2. Fail Safe

a. 概念

首先,在许多地方都没有给出故障安全这个术语,因为Java SE规范没有使用这个术语。我使用这个术语来演示快速失败迭代器和非失败快速迭代器之间的区别。这些迭代器复制内部集合(对象数组)并遍历复制的集合。对迭代器所做的任何结构修改都会影响复制的集合,而不是原始的集合。因此,原始集合在结构上保持不变。

当一个线程使用iterator遍历集合元素时,如果集合的数据被修改了(增、删、改),支持Fail Safe的iterator不会异常。这是因为迭代器是在集合对象的拷贝上操作,而不是直接操作原集合对象。

  • java.util.concurrent下的包如CopyOnWriteArrayList, ConcurrentHashMap都是支持Fail Safe的。
b. 原理
  1. 使用原始集合的副本遍历集合的元素,比如:CopyOnWriteArrayList
// Java code to illustrate 
// Fail Safe Iterator in Java 
import java.util.concurrent.CopyOnWriteArrayList; 
import java.util.Iterator; 
  
class FailSafe { 
    public static void main(String args[]) 
    { 
        CopyOnWriteArrayList<Integer> list 
            = new CopyOnWriteArrayList<Integer>(new Integer[] { 1, 3, 5, 8 }); 
        Iterator itr = list.iterator(); 
        while (itr.hasNext()) { 
            Integer no = (Integer)itr.next(); 
            System.out.println(no); 
            if (no == 8) 
  
                // This will not print, 
                // hence it has created separate copy 
                list.add(14); 
        } 
    } 
} 

OutPut

1
3
5
8

  1. 另外,那些不使用fail-fast概念的集合可能不需要在内存中创建克隆/快照来避免ConcurrentModificationException。例如,在ConcurrentHashMap的情况下,它不会对单独的副本进行操作,尽管它不是Fail Fast。相反,它的语义被官方规范描述为弱一致性(Java中的内存一致性属性)。

ConcurrentHashMap返回的迭代器是弱一致的。这意味着该迭代器可以容忍并发修改,在构造迭代器时遍历元素,并且可以(但不保证)在构造迭代器后反映对集合的修改。

// Java program to illustrate 
// Fail-Safe Iterator which 
// does not create separate copy 
import java.util.concurrent.ConcurrentHashMap; 
import java.util.Iterator; 
  
public class FailSafeItr { 
    public static void main(String[] args) 
    { 
  
        // Creating a ConcurrentHashMap 
        ConcurrentHashMap<String, Integer> map 
            = new ConcurrentHashMap<String, Integer>(); 
  
        map.put("ONE", 1); 
        map.put("TWO", 2); 
        map.put("THREE", 3); 
        map.put("FOUR", 4); 
  
        // Getting an Iterator from map 
        Iterator it = map.keySet().iterator(); 
  
        while (it.hasNext()) { 
            String key = (String)it.next(); 
            System.out.println(key + " : " + map.get(key)); 
  
            // This will reflect in iterator. 
            // Hence, it has not created separate copy 
            map.put("SEVEN", 7); 
        } 
    } 
} 

OutPut

ONE : 1
FOUR : 4
TWO : 2
THREE : 3
SEVEN : 7

3. Fail Fast 和 Fail Safe的区别

主要的区别是,与Fail Fast相反,Fail Safe Iterator不会抛出任何异常。这是因为它们在集合的克隆上工作,而不是在原始集合上工作,这就是它们被称为故障安全迭代器的原因。


参考文章:https://www.geeksforgeeks.org/fail-fast-fail-safe-iterators-java/

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值