ConcurrentHashMap可以说是目前使用最多的并发数据结构之一,作为如此核心的基本组件,不仅仅要满足我们功能的需求,更要满足性能的需求。而实现一个高性能的线程安全的HashMap也绝非易事。
ConcurrentHashMap作为JDK8的内部实现,一个成功的典范,有着诸多可以让我们学习和致敬的地方。
我全局在项目中搜索这个类的时候,发现大量项目代码和源码都用到了,为什么他会这么吃香呢?到底是道德的….呸。
下面我们就来扒一扒,ConcurrentHashMap的内部实现,来体会一下它的精妙之处吧!
ConcurrentHashMap的内部数据结构
在JDK8中, ConcurrentHashMap的内部实现发生了天翻地覆的变化。这里依据JDK8,来介绍一下ConcurrentHashMap的内部实现。
从静态数据结构上说,ConcurrentHashMap包含以下内容:
int sizeCtl
这是一个多功能的字段,可以用来记录参与Map扩展的线程数量,也用来记录新的table的扩容阈值
CounterCell[] counterCells
用来记录元素的个数,这是一个数组,使用数组来记录,是因为避免多线程竞争时,可能产生的冲突。使用了数组,那么多个线程同时修改数量时,极有可能实际操作数组中不同的单元,从而减少竞争。
Node<K,V>[] table
实际存放Map内容的地方,一个map实际上就是一个Node数组,每个Node里包含了key和value的信息。
Node<K,V>[] nextTable
当table需要扩充时,会把新的数据填充到nextTable中,也就是说nextTable是扩充后的Map。
以上就是ConcurrentHashMap的核心元素,其中最值得注意的便是Node,Node并非想象中如此简单,下面的图展示了Node的类族结构:
可以看到,在Map中的Node并非简单的Node对象,实际上,它有可能是Node对象,也有可能是一个Treebin或者ForwardingNode。
那什么时候是Node,什么时候是TreeBin,什么时候又是一个ForwardingNode呢?
其实在绝大部分场景中,使用的依然是Node,从Node数据结构中,不难看出,Node其实是一个链表,也就是说,一个正常的Map可能是长这样的:
上图中,绿色部分表示Node数组,里面的元素是Node,也就是链表的头部,当两个元素在数据中的位
文章出处登录后可见!