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内存模型)有关!    埋个坑?,后面写并发的时候更~ ?



目录
相关文章
|
4天前
|
缓存 NoSQL Redis
Python缓存技术(Memcached、Redis)面试题解析
【4月更文挑战第18天】本文探讨了Python面试中关于Memcached和Redis的常见问题,包括两者的基础概念、特性对比、客户端使用、缓存策略及应用场景。同时,文章指出了易错点,如数据不一致和缓存淘汰策略,并提供了实战代码示例,帮助读者掌握这两款内存键值存储系统的使用和优化技巧。通过理解其核心特性和避免常见错误,可以提升在面试中的表现。
31 2
|
2天前
|
前端开发 JavaScript
Web前端开发之面试题全解析 一,2024年最新面经牛客
Web前端开发之面试题全解析 一,2024年最新面经牛客
|
2天前
|
移动开发 前端开发 JavaScript
Web前端开发之面试题全解析 一(3),前端面试题背不下来怎么办
Web前端开发之面试题全解析 一(3),前端面试题背不下来怎么办
|
2天前
|
前端开发 算法 搜索推荐
Web前端开发之面试题全解析 一(1),2024年最新前端组件化面试
Web前端开发之面试题全解析 一(1),2024年最新前端组件化面试
|
2天前
|
Linux 网络安全
CentOS系统openssh-9,网络安全大厂面试真题解析大全
CentOS系统openssh-9,网络安全大厂面试真题解析大全
|
2天前
|
Android开发
Android高级开发面试题以及笞案整理,实战解析
Android高级开发面试题以及笞案整理,实战解析
|
2天前
|
Android开发
71,字节跳动历年校招Android面试真题解析
71,字节跳动历年校招Android面试真题解析
|
3天前
|
Android开发
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析
|
3天前
|
Android开发 Java Kotlin
阿里P8大佬亲自讲解,android面试题解析
阿里P8大佬亲自讲解,android面试题解析
|
4天前
|
存储 算法 Java
耗时3天写完的HashMap万字解析,争取一篇文章讲透它,面试官看了都直点头!
耗时3天写完的HashMap万字解析,争取一篇文章讲透它,面试官看了都直点头!
51 3

推荐镜像

更多
http://www.vxiaotou.com