concurrenthashmap inittable_JDK8的ConcurrentHashMap

【JUC】JDK1.8源码分析之ConcurrentHashMap(一) - leesf - 博客园​www.cnblogs.com
1e1ded8c61b654316a9bb2053b30af15.png

部分内容转载自,后续我在其基础上做更详细的解释

put方法

public 

说明:put函数底层调用了putVal进行数据的插入,对于putVal函数的流程大体如下。

  ① 判断存储的key、value是否为空,若为空,则抛出异常,否则,进入步骤

  ② 计算key的hash值,随后进入无限循环,该无限循环可以确保成功插入数据,若table表为空或者长度为0,则初始化table表,否则,进入步骤

  ③ 根据key的hash值取出table表中的结点元素,若取出的结点为空(该桶为空),则使用CAS将key、value、hash值生成的结点放入桶中。否则,进入步骤

  ④ 若该结点的的hash值为MOVED,则对该桶中的结点进行转移,否则,进入步骤

  ⑤ 对桶中的第一个结点(即table表中的结点)进行加锁,对该桶进行遍历,桶中的结点的hash值与key值与给定的hash值和key值相等,则根据标识选择是否进行更新操作(用给定的value值替换该结点的value值),若遍历完桶仍没有找到hash值与key值和指定的hash值与key值相等的结点,则直接新生一个结点并赋值为之前最后一个结点的下一个结点。进入步骤

  ⑥ 若binCount值达到红黑树转化的阈值,则将桶中的结构转化为红黑树存储,最后,增加binCount的值。

  在putVal函数中会涉及到如下几个函数:initTable、tabAt、casTabAt、helpTransfer、putTreeVal、treeifyBin、addCount函数。下面对其中涉及到的函数进行分析。

重要成员变量

1、siseCtl:在多个方法中出现过这个变量,该变量主要是用来控制数组的初始化和扩容的,默认值为0,可以概括一下4种状态:

  • a、sizeCtl=0:默认值;
  • b、sizeCtl=-1:表示Map正在初始化中;
  • c、sizeCtl=-N:表示正在有N-1个线程进行扩容操作;
  • d、sizeCtl>0: 未初始化则表示初始化Map的大小,已初始化则表示下次进行扩容操作的阈值;

2、table:用于存储链表或红黑数的数组,初始值为null,在第一次进行put操作的时候进行初始化,默认值为16;

3、nextTable:在扩容时新生成的数组,其大小为当前table的2倍,用于存放table转移过来的值;

4、Node:该类存储数据的核心,以key-value形式来存储;

5、ForwardingNode:这是一个特殊Node节点,仅在进行扩容时用作占位符,表示当前位置已被移动或者为null,该node节点的hash值为-1;

final 

其中 initTable函数源码如下

/**   

说明:对于table的大小,会根据sizeCtl的值进行设置,如果没有设置szieCtl的值,那么默认生成的table大小为16,否则,会根据sizeCtl的大小设置table大小。

关于initTable函数,我在知乎上提了个问题

JDK1.8中ConcurrentHashMap的初始化竞争失败的线程为什么需要yield,有意义吗?​www.zhihu.com

 tabAt函数源码如下

static 

说明:此函数返回table数组中下标为i的结点,可以看到是通过Unsafe对象通过反射获取的,getObjectVolatile的第二项参数为下标为i的偏移地址。

你可能会有疑问为什么使用这个方法而不是数组下标直接获取?

jdk7中关于并发map的读不加锁,如何理解下面这句话?​www.zhihu.com

读取到null的问题是初始化HashEntry时发生的指令重排序导致的,也就是在HashEntry初始化完成之前便返回了它的引用。1.6的加lock来解决,乐观锁CAS(compare and swap)是处理器提供的原语,能保证禁止该指令的指令重排序,且能把写缓冲区的所有数据刷新到内存中。

casTabAt函数源码如下 

static 

说明:此函数用于比较table数组下标为i的结点是否为c,若为c,则用v交换操作。否则,不进行交换操作。

helpTransfer函数源码如下

final 

说明:此函数用于在扩容时将table表中的结点转移到nextTable中。

putTreeVal函数源码如下

final 

说明:此函数用于将指定的hash、key、value值添加到红黑树中,若已经添加了,则返回null,否则返回该结点。

treeifyBin函数源码如下

private 

 说明:此函数用于将桶中的数据结构转化为红黑树,其中,值得注意的是,当table的长度未达到阈值时,会进行一次扩容操作,该操作会使得触发treeifyBin操作的某个桶中的所有元素进行一次重新分配,这样可以避免某个桶中的结点数量太大。

addCount函数源码如下

private 

说明:此函数主要完成binCount的值加1的操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值