关于java:java集合的快速失败和安全失败

9次阅读

共计 2485 个字符,预计需要花费 7 分钟才能阅读完成。

引言

明天又是折磨人的一天,原本在钻研为什么 ConcurrentHashMap 为什么 key 和 value 为什么不能为 null 的问题的,后果百度查出来的答案有这么一个:

ConcurrentHashMap 是平安失败机制的,使你此次读到的数据不肯定是最新的数据。无奈判断对应的 key 是不存在还是为空。

这看的我一愣一愣的,了解不能,就先写一下疾速失败和平安失败来坚固下知识点。

疾速失败

定义

在应用迭代器遍历一个对象时,遍历过程中对汇合内容进行了批改时,会抛出 Concurrent Modification Exception

原理


416 行:HashMap 源码外面有一个 modCount 计数器


显示下所有对于 modCount 的操作,能够看到只有自增操作,没有自减操作


1425 行:迭代器结构的时候,先用另外的空间,保留 modCount 的值
1441 行:HashMap 提供的迭代器,比拟了 modCount 和 expectedModCoun 值是否相等,不相等就抛出异样

平安失败

定义

在应用迭代器遍历一个对象时,遍历过程中对汇合内容进行了批改时,不会抛出 Concurrent Modification Exception,且原有对象能批改胜利

原理

我原本是想写一下原理的,起初看了下平安失败代码汇合的源码,ConcurrentHashMap 和 HashTable,发现两者原理实现上还是有区别的。网络上查问平安失败机制广泛是这样形容的

平安失败是对汇合的拷贝进行遍历,遍历过程中对原汇合的批改不会被迭代器检测到

这个导致我认为 ConcurrentHashMap 获取迭代器的时候,也是拷贝了一份,后果花了大把的工夫看了 ConcurrentHashMap 的源码,发现压根找不到哪里有复制的。(基于 jdk1.8)

ConcurrentHashMap 代码测试平安失败(基于 jdk1.8)

    public static void main(String[] args) {ConcurrentHashMap<String, Integer> concurrentHashMap = new ConcurrentHashMap<>(16);
        concurrentHashMap.put("空想 4", 4);
        concurrentHashMap.put("空想 2", 2);
        concurrentHashMap.put("空想 3", 3);

        Set<Map.Entry<String, Integer>> set = concurrentHashMap.entrySet();
        Iterator<Map.Entry<String, Integer>> iterator = set.iterator();

        while (iterator.hasNext()) {System.out.println(iterator.next());
            concurrentHashMap.put("空想 1", 1);
        }

        System.out.println("程序完结");
    }

下面是我本人写的一个测试代码,运行下,用断点来看下具体发送了什么。

75 行的断点,76 行会加一个值进去,在 75 行的断点能够看到,set 是 3 个值

通过一次 76 行当前,set 的值就变成了 4 个,能够分明的看到 ConcurrentHashMap 相对不是对汇合的拷贝进行的遍历。上面贴上运行后果:

如果是对汇合的拷贝进行遍历,为什么还会输入遍历过程才退出的“空想 1”呢?
具体为什么要看 ConcurrentHashMap 的源码来解释了,之后有空再写一篇 ConcurrentHashMap 的源码解析。

HashTable 平安失败测试

    public static void main(String[] args) {Hashtable<String, Integer> hashtable = new Hashtable<>(16);
        hashtable.put("空想 4", 4);
        hashtable.put("空想 2", 2);
        hashtable.put("空想 3", 3);
        Set<Map.Entry<String, Integer>> set = hashtable.entrySet();
        Iterator<Map.Entry<String, Integer>> iterator = set.iterator();
        while (iterator.hasNext()) {System.out.println(iterator.next());
            hashtable.put("空想 1", 1);
        }
        System.out.println("程序完结");
    }

下面是我的测试代码

这是运行后果,抛出了 Concurrent Modification Exception。
我这么一看,不对啊,说好的平安失败是在复制的汇合上遍历的呢?怎么还呈现了疾速失败的异样?
兄弟们是不是想晓得?别急,我再丢个问题给你们思考下。

 public static void main(String[] args) {Hashtable<String, Integer> hashtable = new Hashtable<>(16);
        hashtable.put("空想 4", 4);
        hashtable.put("空想 2", 2);
        hashtable.put("空想 3", 3);
        Enumeration<String> keys = hashtable.keys();
        while (keys.hasMoreElements()) {System.out.println(keys.nextElement());
            hashtable.put("空想 1", 1);
        }
        System.out.println("程序完结");
    }

对之前的代码批改下迭代器,用了个 HashTable 自带的一个非凡迭代器,运行后果如下

哎,这么一看就变成和 ConcurrentHashMap 的迭代后果差不多了。

论断

我也不晓得怎么下结论,如果平安失败机制真的要拷贝一份的话,我感觉 ConcurrentHashMap 和 HashMap 都不是走平安失败机制。

ps:ConcurrentHashMap 源码挖坑
pps:HashTable 源码挖坑

正文完
 0