fail-safe 和 fail-fast

2024-04-16 19:48
文章标签 fail safe fast

本文主要是介绍fail-safe 和 fail-fast,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

你真的了解 fail-fastfail-safe 吗?👍

简介

java.util   包下的 属于  fail-fast    , 快速失败~ 😝    

java.util.concurrent   包下的 属于  fail-safe   ,安全失败~ 😝

简单来说 就是    fail-fast   在迭代时,如果发现 该集合数据 结构被改变 (modCount != expectedModCount),就会 抛出 ConcurrentModificationException  

小伙伴们可以参考下 下面的代码简单实验一下~ 😋

fail-fast 实验代码

实验对象是 Hashtable,这里采用 jdk1.7 的写法 ~  

因为博主还在研究 下文中 ConcurrentHashMap 在7和8中有啥不一样 😝

 

class E implements Runnable{

    Hashtable<String, String> hashtable;

    public E(Hashtable<String, String> hashtable) {
        this.hashtable = hashtable;
    }

    private void add(Hashtable<String, String> hashtable){
        for (int i = 0; i < 10000000; i++) {
            hashtable.put("a",""+i);
        }
    }

    @Override
    public void run() {
        add(hashtable);
    }
}

public class D {


    public static void main(String[] args) {
        Hashtable<String, String> hashtable = new Hashtable<String, String>();
        hashtable.put("1","2");
        hashtable.put("2","2");
        hashtable.put("3","2");
        hashtable.put("4","2");
        hashtable.put("15","2");


        new Thread(new E(hashtable)).start();

        Set<Map.Entry<String, String>> entries = hashtable.entrySet();
        Iterator<Map.Entry<String, String>> iterator = entries.iterator();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        while (iterator.hasNext()){
            System.out.println(iterator.next());
            iterator.remove();
        }
    }
}

效果如图:

图片

触发的原理:

图片

当集合数据结构发生变化时,这两个值是不相等的,所以会抛出该异常~ 。

结论:

虽然 HashTable 是 线程安全的  , 但是它有  fail-fast 机制  ,所以在多线程情况下进行 迭代 也不能去修改它的数据结构!

fail-fast 机制 不允许并发修改! 

fail-safe  实验代码

 

class E implements Runnable{

    ConcurrentHashMap<String, String> concurrentHashMap;

    public E(ConcurrentHashMap<String, String> concurrentHashMap) {
        this.concurrentHashMap = concurrentHashMap;
    }

    private void add( ConcurrentHashMap<String, String> concurrentHashMap){
        for (int i = 0; i < 100000; i++) {
            concurrentHashMap.put("a"+i,""+i);
        }
    }

    @Override
    public void run() {
        add(concurrentHashMap);
    }
}
public class D {



    public static void main(String[] args) {

        ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap<String, String>();

        concurrentHashMap.put("1","2");
        concurrentHashMap.put("2","2");
        concurrentHashMap.put("3","2");
        concurrentHashMap.put("4","2");
        concurrentHashMap.put("15","2");

        new Thread(new E(concurrentHashMap)).start();
        try {
            Thread.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Set<Map.Entry<String, String>> entries = concurrentHashMap.entrySet();


        for (Map.Entry<String, String> entry : entries) {
            System.out.println(entry);
//            这里不用调用 iterator 去 remove
            concurrentHashMap.remove(entry.getKey());
        }

    }
}

效果如图:

图片

代码运行讲解,线程A 往里加数据,线程B 遍历它的数据,并删除。

可以看到这里并没有报错~,但是它也不能保证遍历到所有的值 (可以理解为无法获取到最新的值)

有没有感受到一丝丝 安全失败的感觉~ 😄

哈哈哈 它的特点就是  👉 允许并发修改,不会抛出 ConcurrentModificationException ,但是无法保证拿到的是最新的值

图片

不知道小伙伴们看完上面的实验代码有没有疑惑

(・∀・(・∀・(・∀・*)

为什么可以调用它自身的 remove 呢?

别急~ 我们先来看看使用这个迭代器中发生了什么?

源码走起~

小伙伴们可以看看下面四张图~

图片

创建迭代器的过程

图片

图片

图片

图片

图一 可以看到会去创造一个 EntryIterator, 而 它又 继承了 HashIterator ,在初始化时,会先调用父类的构造器。

图三 可以发现  HashIterator  在初始化 时,会去调用 advance 方法 (这里就不展开这个 concurrentHashMap结构啦~ ) 这里的重点在最后一张图 , 它调用的是 UNSAFE.getObjectVolatile

它的作用是 强制从主存中获取属性值。 

小伙伴们可以自行对比下 HashMap 或者 上面的 HashTable,他们都是直接 拿到代码中定义的这个 Entry[]~。🐷

图片

不知道小伙伴们 get 得到这个点没有~  

哈哈哈 容我唠叨唠叨一下~ 😝

4ye 在网上搜这个 fail-fast 和  fail-safe 的区别时,看到下面这张图。

几乎都在说 fail-safe 会复制原来的集合,然后在复制出来的集合上进行操作,然后就说这样是不会抛出 ConcurrentModificationException 异常了。

可是这种说法是 不严谨的~ 😝  它描述的情况应该是针对这个  CopyOnWriteArrayList  或者 CopyOnWriteArraySet 的情况(下面的源码讲到~)

图片

CopyOnWriteArrayList  源码

可以发现这里 snapshot 的指针是始终指向这个原数组的(当你创建迭代器的时候)

图片

当你添加数据时,它会复制原来的数组,并在复制出来的数组上进行修改,

然后再设置进去,可以发现至始至终都没有修改到这个原数组,

所以迭代器中的数据是不受影响的~😝

图片

结论

fail-safe  也是得具体情况具体分析的。

  1. 如果是  CopyOnWriteArrayList  或者 CopyOnWriteArraySet  ,就属于 复制原来的集合,然后在复制出来的集合上进行操作 的情况 ,所以是不会抛出这个 ConcurrentModificationException  的 。

  2. 如果是这个 concurrentHashMap 的,就比较硬核了~ 😄 它直接操作底层,调用UNSAFE.getObjectVolatile  ,直接  强制从主存中获取属性值,也是不会抛出这个 ConcurrentModificationException  的 。

  3. 并发下,无法保证 遍历时拿到的是最新的值~


嘿嘿 现在回答上面那个 为啥可以 remove 的问题啦~

remove 的源码如下

图片

图片

重点在红框处, pred 为 null 表示是数组的头部,此时调用 setEntryAt ,这里也是出现了这个  UNSAFE 😋

UNSAFE.putOrderedObject 这段代码的意思就是 :

有序的(有延迟的) 强制 更新数据到 主内存。(不能立刻被其他线程发现) 

这些 和 Java 的 JMM (Java内存模型)有关!   

总结

java.util 包下的属于fail-fast

特点:

不允许并发修改,如果并发修改的话会导致在迭代过程中抛出 ConcurrentModificationException  ,

触发点是 modCount != expectedModCount😝

java.util.concurrent 包下的 属于  fail-safe  ,

特点:

允许并发修改,但是 无法保证在迭代过程中获取到最新的值 。 😋  

concurrentHashMap  获取和修改数据时 ,是通过 UNSAFE 类 直接从主内存中获取或者更新数据到主内存~

图片

CopyOnWriteArrayList  或者 CopyOnWriteArraySet  ,就直接 复制原来的集合,然后在复制出来的集合上进行操作

图片

这篇关于fail-safe 和 fail-fast的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/909741

相关文章

每天一道面试题(2):fail-safe 机制与 fail-fast 机制分别有什么作用?

当谈论Java集合的 fail-fast 和 fail-safe 机制时,涉及的是在集合被并发修改时的行为和处理方式。这些机制对保证程序的正确性和稳定性非常重要,尤其是在多线程环境中。 1. Fail-Fast 机制 定义: Fail-fast 机制的核心是在检测到集合在遍历过程中被修改时,立即抛出 ConcurrentModificationException 异常,从而中断迭代操作。这种

解决PHP Warning: strftime(): It is not safe to rely on the system's timezone set

当运行一些程序时,在httpd日志中会有如下警告日志: PHP Warning:  strftime(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set(

UVa 11992 Fast Matrix Operations 线段树

UVa 11992 Fast Matrix Operations 题目大意:有一个r行c列的全0矩阵,支持三种操作: 1 x1 y1 x2 y2 v 子矩阵(x1,y1,x2,y2)的所有元素增加v(v > 0)。 2 x1 y1 x2 y2 v 子矩阵(x1,y1,x2,y2)的所有元素设为v(v > 0)。 3 x1 y1 x2 y2    查询子矩阵(x1,y1,x2,y2

【HDU】4965 Fast Matrix Calculation 矩阵快速幂

传送门:【HDU】4965 Fast Matrix Calculation 题目分析:因为比赛的时候写的太匆忙。。写的不堪入目,所以赛后重写了一次,顺便就贴一下了。 因为A*B=C,所以C^(N*N-1) = A*B*A*B*A*...*B*A*B,因为满足结合律所以变成A*( (B*A)^(N*N-2) )*B,因为中间得到的矩阵最大不超过K(K<=6),所以可以对中间的矩阵快速幂,然

Fast Image Cache

https://github.com/path/FastImageCache   Fast Image Cache is an efficient, persistent, and—above all—fast way to store and retrieve images in your iOS application. Part of any good iOS applica

NCBI-get-GCFIDs_fast.py

import requestsimport osimport redef download_genome_first(gcf_id):# 构建FTP下载路径base_url = "https://ftp.ncbi.nlm.nih.gov/genomes/all/GCF/"# 提取GCF号的数字部分并按三位分割parts = gcf_id.split('_')[1] # 提取数字部分path_

Fast Power

Calculate the an % b where a, b and n are all 32bit non-negative integers. Example For 231 % 3 = 2 For 1001000 % 1000 = 0 Challenge O(logn) 思想:recursion算一半,然后base case,处理算完一半以后的情况; 公式就是 (a*b) %

getLocation:fail, the permission value is offline verifying

getLocation:fail, the permission value is offline verifying 后端会根据appid和secret生成 签名,前端wx配置时一定用appid来验证签名的正确 本次错误为配置初始化失败:前端与后端的appId不一致,我的失误也

fast-voice-assistant

首先我们来到这个据说50行代码就可以创建个人语音助手的github地址GitHub - dsa/fast-voice-assistant: ⚡ Insanely fast AI voice assistant with <500ms response times 按照readme 完成环境的配置 but,你发现,这只是第一步,真正的难点在于完成.env中各个key的配置 1)Using th

[目标检测]Fast RCNN算法详解

转载来自:http://blog.csdn.net/shenxiaolu1984/article/details/51036677 继2014年的RCNN之后,Ross Girshick在15年推出Fast RCNN,构思精巧,流程更为紧凑,大幅提升了目标检测的速度。在Github上提供了源码。 同样使用最大规模的网络,Fast RCNN和RCNN相比,训练时间从84小时减少为9.5小时