fail-safe 和 fail-fast 硬核解析,让你和面试官多聊十分钟!

简介: 简介java.util 包下的 属于 fail-fast , 快速失败~ 😝java.util.concurrent 包下的 属于 fail-safe ,安全失败~ 😝简单来说 就是 fail-fast 在迭代时,如果发现 该集合数据 结构被改变 (modCount != expectedModCount),就会 抛出 ConcurrentModificationException小伙伴们可以参考下 下面的代码简单实验一下~ 😋fail-fast 实验代码实验对象是 Hashtable,这里采用 jdk1.7 的写法 ~因为博主还在研究 下文

简介


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  的 。


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


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




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


remove 的源码如下


网络异常,图片无法展示
|


网络异常,图片无法展示
|


网络异常,图片无法展示
|


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


现了这个  UNSAFE 😋 , setNext 也一样~


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


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


这些和 Java 的 JMM (Java内存模型)有关!    埋个坑🕳,后面写并发的时候更~ 😝



目录
相关文章
|
14天前
|
SQL 分布式计算 监控
Sqoop数据迁移工具使用与优化技巧:面试经验与必备知识点解析
【4月更文挑战第9天】本文深入解析Sqoop的使用、优化及面试策略。内容涵盖Sqoop基础,包括安装配置、命令行操作、与Hadoop生态集成和连接器配置。讨论数据迁移优化技巧,如数据切分、压缩编码、转换过滤及性能监控。此外,还涉及面试中对Sqoop与其他ETL工具的对比、实际项目挑战及未来发展趋势的讨论。通过代码示例展示了从MySQL到HDFS的数据迁移。本文旨在帮助读者在面试中展现Sqoop技术实力。
27 2
|
14天前
|
监控 负载均衡 Cloud Native
ZooKeeper分布式协调服务详解:面试经验与必备知识点解析
【4月更文挑战第9天】本文深入剖析ZooKeeper分布式协调服务原理,涵盖核心概念如Server、Client、ZNode、ACL、Watcher,以及ZAB协议在一致性、会话管理、Leader选举中的作用。讨论ZooKeeper数据模型、操作、会话管理、集群部署与管理、性能调优和监控。同时,文章探讨了ZooKeeper在分布式锁、队列、服务注册与发现等场景的应用,并在面试方面分析了与其它服务的区别、实战挑战及解决方案。附带Java客户端实现分布式锁的代码示例,助力提升面试表现。
30 2
|
14天前
|
数据采集 消息中间件 监控
Flume数据采集系统设计与配置实战:面试经验与必备知识点解析
【4月更文挑战第9天】本文深入探讨Apache Flume的数据采集系统设计,涵盖Flume Agent、Source、Channel、Sink的核心概念及其配置实战。通过实例展示了文件日志收集、网络数据接收、命令行实时数据捕获等场景。此外,还讨论了Flume与同类工具的对比、实际项目挑战及解决方案,以及未来发展趋势。提供配置示例帮助理解Flume在数据集成、日志收集中的应用,为面试准备提供扎实的理论与实践支持。
25 1
|
2天前
|
存储 算法 Java
耗时3天写完的HashMap万字解析,争取一篇文章讲透它,面试官看了都直点头!
耗时3天写完的HashMap万字解析,争取一篇文章讲透它,面试官看了都直点头!
26 3
|
6天前
|
数据采集 机器学习/深度学习 数据挖掘
Python数据清洗与预处理面试题解析
【4月更文挑战第17天】本文介绍了Python数据清洗与预处理在面试中的常见问题,包括Pandas基础操作、异常值处理和特征工程。通过示例代码展示了数据读取、筛选、合并、分组统计、离群点检测、缺失值和重复值处理、特征缩放、编码、转换和降维。强调了易错点,如忽视数据质量检查、盲目处理数据、数据隐私保护、过度简化特征关系和忽视模型输入要求。掌握这些技能和策略将有助于在面试中脱颖而出。
23 8
|
8天前
|
调度 Python
Python多线程、多进程与协程面试题解析
【4月更文挑战第14天】Python并发编程涉及多线程、多进程和协程。面试中,对这些概念的理解和应用是评估候选人的重要标准。本文介绍了它们的基础知识、常见问题和应对策略。多线程在同一进程中并发执行,多进程通过进程间通信实现并发,协程则使用`asyncio`进行轻量级线程控制。面试常遇到的问题包括并发并行混淆、GIL影响多线程性能、进程间通信不当和协程异步IO理解不清。要掌握并发模型,需明确其适用场景,理解GIL、进程间通信和协程调度机制。
28 0
|
8天前
|
API Python
Python模块化编程:面试题深度解析
【4月更文挑战第14天】了解Python模块化编程对于构建大型项目至关重要,它涉及代码组织、复用和维护。本文深入探讨了模块、包、导入机制、命名空间和作用域等基础概念,并列举了面试中常见的模块导入混乱、不适当星号导入等问题,强调了避免循环依赖、合理使用`__init__.py`以及理解模块作用域的重要性。掌握这些知识将有助于在面试中自信应对模块化编程的相关挑战。
21 0
|
14天前
|
机器学习/深度学习 分布式计算 BI
Flink实时流处理框架原理与应用:面试经验与必备知识点解析
【4月更文挑战第9天】本文详尽探讨了Flink实时流处理框架的原理,包括运行时架构、数据流模型、状态管理和容错机制、资源调度与优化以及与外部系统的集成。此外,还介绍了Flink在实时数据管道、分析、数仓与BI、机器学习等领域的应用实践。同时,文章提供了面试经验与常见问题解析,如Flink与其他系统的对比、实际项目挑战及解决方案,并展望了Flink的未来发展趋势。附带Java DataStream API代码样例,为学习和面试准备提供了实用素材。
37 0
|
14天前
|
分布式计算 资源调度 监控
Hadoop生态系统深度剖析:面试经验与必备知识点解析
本文深入探讨了Hadoop生态系统的面试重点,涵盖Hadoop架构、HDFS、YARN和MapReduce。了解Hadoop的主从架构、HDFS的读写流程及高级特性,YARN的资源管理与调度,以及MapReduce编程模型。通过代码示例,如HDFS文件操作和WordCount程序,帮助读者巩固理解。此外,文章强调在面试中应结合个人经验、行业动态和技术进展展示技术实力。
|
19天前
|
自然语言处理
大型语言模型(LLMs)面试常见问题解析
大型语言模型(LLMs)面试常见问题解析
31 4

推荐镜像

更多