JDK1.8中的ConcurrentHashMap源码分析
一、容器初始化
1、源码分析
在jdk8的concurrenthashmap中一共有5个构造方法,这四个构造方法中都没有对内部的数组做初始化, 只是对一些变量的初始值做了处理
jdk8的concurrenthashmap的数组初始化是在第一次添加元素时完成
注意,调用这个方法,得到的初始容量和hashmap以及jdk7的concurrenthashmap不同,即使你传递的是一个2的幂次方数,该方法计算出来的初始容量依然是比这个值大的2的幂次方数
2、sizectl
含义解释
注意:以上这些构造方法中,都涉及到一个变量
sizectl
,这个变量是一个非常重要的变量,而且具有非常丰富的含义,它的值不同,对应的含义也不一样,这里我们先对这个变量不同的值的含义做一下说明,后续源码分析过程中,进一步解释
sizectl
为0,代表数组未初始化, 且数组的初始容量为16
sizectl
为正数,如果数组未初始化,那么其记录的是数组的初始容量,如果数组已经初始化,那么其记录的是数组的扩容阈值
sizectl
为-1,表示数组正在进行初始化
sizectl
小于0,并且不是-1,表示数组正在扩容, -(1+n),表示此时有n个线程正在共同完成数组的扩容操作
3、其他属性含义
代表整个哈希表
用于哈希表扩容,扩容完成后会被重置为null。
basecount和countercells一起保存着整个哈希表中存储的所有的结点的个数总和。
二、添加安全
1、源码分析
1.1、添加元素put/putval方法
通过以上源码,我们可以看到,当需要添加元素时,会针对当前元素所对应的桶位进行加锁操作,这样一方面保证元素添加时,多线程的安全,同时对某个桶位加锁不会影响其他桶位的操作,进一步提升多线程的并发效率
1.2、数组初始化,inittable方法
2、图解
2.1、put加锁图解
三、扩容安全
1、源码分析
2、图解
四、多线程扩容效率改进(协助扩容)
多线程协助扩容的操作会在两个地方被触发:
① 当添加元素时,发现添加的元素对用的桶位为fwd节点,就会先去协助扩容,然后再添加元素
② 当添加完元素后,判断当前元素个数达到了扩容阈值,此时发现sizectl的值小于0,并且新数组不为空,这个时候,会去协助扩容
每当有一个线程帮助扩容时,sc就会+1,有一个线程扩容结束时,sc就会-1,当sc重新回到(rs << resize_stamp_shift) + 2这个值时,代表当前线程是最后一个扩容的线程,则扩容结束。
1、源码分析
1.1、元素未添加,先协助扩容,扩容完后再添加元素
1.2、先添加元素,再协助扩容
注意:扩容的代码都在transfer方法中,这里不再赘述
2、图解
五、集合长度的累计方式
1、源码分析
1.1、addcount方法
① countercell数组不为空,优先利用数组中的countercell记录数量
② 如果数组为空,尝试对basecount进行累加,失败后,会执行fulladdcount逻辑
③ 如果是添加元素操作,会继续判断是否需要扩容
1.2、fulladdcount方法
① 当countercell数组不为空,优先对countercell数组中的countercell的value累加
② 当countercell数组为空,会去创建countercell数组,默认长度为2,并对数组中的countercell的value累加
③ 当数组为空,并且此时有别的线程正在创建数组,那么尝试对basecount做累加,成功即返回,否则自旋
2、图解
fulladdcount方法中,当as数组不为空的逻辑图解
六、集合长度获取
1、源码分析
1.1、size方法
1.2、sumcount方法
注意:这个方法并不是线程安全的
七、get方法
这个就很简单了,获得hash值,然后判断存在与否,遍历链表即可,注意get没有任何锁操作!
到此这篇关于jdk1.8中的concurrenthashmap源码分析的文章就介绍到这了,更多相关jdk1.8 concurrenthashmap源码内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
上一篇: Photoshop 漂亮的绿茶海报
下一篇: VUE中使用MUI方法