简单聊聊JDK8下ConcurrentHashMap的put方法
最近又看了一些关于Java8下ConcurrentHashMap的分析,自己也尝试通过源码来进行一些理解,这里总结性的记录下一些收获,更详细的资料,网上很多,再此就不造轮子了。
本文主要记录put方法相关的几个点:
sizectl的作用?初始化tab数组时,如何保证线程安全的?初始化tab[i]的时候,如何保证线程安全的?遍历的时候,如何保证线程安全?addCount,如何保证线程安全的。JDK7的乐观+悲观策略。
路漫漫其修远兮,吾将上下而求索!
sizeCtl的作用
首先看其定义,其中关键字时 volatile 保证了线程可见性。
该字段主要起到了一个 状态描述 和 扩容阈值作用。
等于 0: 表示默认值大于 0:扩容阈值等于 -1:正在初始化小于 -1: 多个线程正在扩容
在ConcurrentHashMap中的几个操作中就利用了该字段的状态做判定,接着看。
初始化数组
HashMap的结构最外层是数组,这点大家应该都知道了,那么在当这个数组为null的时候,需要对其初始化,CCHM(ConcurrentHashMap)是如何做的?
上述代码中,有3个关键:
if ((sc = sizeCtl) < 0)判定当前是否有线程正在初始化,如果有,那么通过Thread.yield()时间片。通过CAS赋值,代码U.compareAndSwapInt(this, SIZECTL, sc, -1).外层的while循环保证必须初始化成功再退出。
初始化tab[i]
初始化完成tab后,通过key的hashcode计算出当前值存在再下标为i的数组中了,第一次插入的时候,tab[i]肯定为null,那么如何初始化了?
其中casTabAt为:
标准的CAS赋值,保证只会被初始化一次。
遍历判定
找到对应的数组下标后,就需要进行遍历链表或者红黑树,找出该值是否已经存在。
java.util.concurrent.ConcurrentHashMap#putVal
大致代码如所述:很容易看出来,通过了关键字synchronized锁住了列表第一个值。
addCount
addCount的作用是维护 CCHM的size,其代码如下所示:
U.compareAndSwapLong通过这个很明显看出来是CAS
JDK7的乐观和悲观
最后简单提下JDK7在put和size的实现,很有趣,其内部是:先是乐观尝试,达到一定的阈值都没有成功就转为悲观的加锁操作。
scanAndLockForPut代码如下:
size的代码如下:
很有意思吧,先乐观后悲观。
总结
上述这些就是最近看JDK7和JDK8的源码的一些个人理解。
其实结合原理来看,并不难。
路漫漫其修远兮,吾将上下而求索!
原创不易,感谢关注、评论、转发、收藏!
查看原文 >>