JDK1.8 ConcurrentHashMap 核心源码(面试重点)
一、什么是 ConcurrentHashMap
ConcurrentHashMap 和 HashMap 一样,是一个存放键值对的容器。使用 hash 算法来获取值的地址,因此时间复杂度是 O(1)。查询非常快。
同时,ConcurrentHashMap 是线程安全的 HashMap。主要通过对指定的 Node 节点加锁,来保证数据更新的安全性。
二、ConcurrentHashMap 原理
在 JDK1.8 里面,ConcurrentHashMap 的数据结构为:数组+链表+红黑树。
其线程安全主要在两方面实现,一是数组使用 volatile 关键字,二是使用 CAS 自旋锁实现原子操作。
三、ConcurrentHashMap 源码详解
volatile:修饰节点数组,保证其可见性,禁止指令重排。
DCL 操作:Double Check Lock 也是双重检查锁
一般情况下,大家在了解 DCL 时,都是通过单例模式的懒汉机制了解到的。
ConcurrentHashMap 中的数组有一个就够了,数组在 ConcurrentHashMap 中就是单例的。同时 ConcurrentHashMap 在刚刚 new 完之后,不会初始化数组,而是第一个添加数据时,才会初始化数组。
look 源码去:
发现初始化数组的方法是 initTable,在 initTable 的过程中,会涉及到一个属性,sizeCtl。
initTable 方法
CAS:实现并发原子操作
ConcurrentHashMap 之 put 操作
casTabAt 方法内部即 CAS 操作
CAS(Compare-And-Swap):比较并交换。通过一个原子操作,用预期值去和实际值做对比,如果实际值和预期相同,则做更新操作。如果预期值和实际不同,就认为其他线程更新了这个值,不做更新操作。
总结
做插入操作时,首先进入乐观锁,
然后,在乐观锁中判断容器是否初始化,
如果没初始化则初始化容器,
如果已经初始化,则判断该 hash 位置的节点是否为空,如果为空,则通过 CAS 操作进行插入。
如果该节点不为空,再判断容器是否在扩容中,如果在扩容,则帮助其扩容。
如果没有扩容,则进行最后一步,先加锁,然后找到 hash 值相同的那个节点(hash 冲突),
循环判断这个节点上的链表,决定做覆盖操作还是插入操作。
循环结束,插入完毕。
评论