ConcurrentHashMap源码分析

我们从ConcurrentHashMap的带参的构造方法为入口进行源码的分析,首先看到它的构造方法。

cap为初始容量,由上可知,当初始容量大于等于最大容量的一半时则map的初始容量为最大的容量,否则通过tableSizeFor这个方法进行初始容量的计算,我们可以进入方法看一下初始容量是怎么计算的。

意思就是获取比传入的容量大的并且是2的次幂的数。容量设置为2的次幂也是为了日后做扩容的时候方便。 回到构造方法,最后一行代码是将sizeCtl参数赋值为cap,sizeCtl这个参数是值扩容的阈值,这里只是给它赋一个值,后面并非就是这个初始容量值。

通过构造方法可知,构造方法中只是计算了初始容量的值,并没有初始化map的容器对象。接着我么就去看一下ConcurrentHashMap的put方法。

put方法里面调用了putVal方法,我们来进入putVal方法详细的看一下ConcurrentHashMap是如何向容器中添加数据的。

 从这个方法里面我们也可以得知, ConcurrentHashMap是通过一个table也就是一个Node<K,V>[]来存储数据的。

可以看到这个Node其实就是一个Map.Entry,相当于一个链表结构。

继续回到put方法中,方法中第一个if先是判断的存储数据的table容器是否为空,如果为空则调用initTable方法进行一个初始化的动作,现在知道构造方法中只是做了一个初始容量的计算,其实把容器的初始化动作放到了第一次调用put方法的时候进行了。进入initTable方法。

 这里是采用了cas操作来保证有多个线程进行put操作时容器只会初始化一次。 初始化完成之后将sizeCtl设置成0.75n,表示下一次扩容的阈值。

查看put方法的第二个if,这里是通过hash和数组长度mod之后计算得到的位置上并没有数据,则创建一个Node。

put方法的第三个if,如果正在扩容则帮助扩容,扩容方法我们后面再看,先看最后一个if分支,这里是如果存在hash冲突的情况,则添加到当前的Node下面,具体的方法内容如下

注意这个synchronized同步锁,它锁住的是当前的这个Node链表,由此可以知道ConcurrentHashMap的锁粒度为一个Node<K,V>。查看同步代码块中的判读,如果当前Node的hash值是大于等于0的,说明当前的ConcurrentHashMap还是一个数组加链表的结构。如果是链表则遍历链表,如果有key值一样的则覆盖当前的值,否则新增一个在链表后面加一个数据。随着遍历这里的binCount的变量是一直在累加的。再看下面第二个if,如果当前的节点是一个红黑树结构了,则通过红黑树去添加元素。

注意最后一段红色框中的代码,这个会最终判断binCount的值的大小,如果该值大于等于8的时候则链表会转换为红黑树。我们进入转换的方法看一下。

先判断当前map的当前数组的长度是否是大于64,如果数组长度没有大于64则不做链表到红黑树的转换,只做数组的扩容。

 这里先是判断是否初始化了容器对象,如果没有初始化则进行一个初始化,如果初始化过了则最终会调用一个transfer方法。transfer中是实现扩容的具体逻辑。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值