引言

明天又是折磨人的一天,原本在钻研为什么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源码挖坑