part12-并发容器JDK1.8版本ConcurrentHashMap

1. 小声哔哔

    今天来嗨一下JDK1.8的ConcurrentHashMap,JDK1.8和JDK1.7的ConcurrentHashMap最大的区别就是JDK1.8去除了segment,直接使用Node数组加链表的数据结构,而且链表过长会转换为红黑树,其中链表转红黑树的阀值是在一个链表挂的个数超过8个的时候会转换为红黑树。

    JDK1.8版本的ConcurrentHashMap有6000+代码,所以我只看常用的,对红黑树的处理就领会精神吧。

1.1. 主要参数及内部类

  • 内部类:Node类用户存放实际的key和value值
  • sizeCtl:

        负数:表示进行初始化或者扩容,-1表示正在初始化,-N,表示有N-1个线程正在进行扩容

        正数:0 表示还没有被初始化,>0的数,初始化或者是下一次进行扩容的阈值

  • TreeNode 用在红黑树,表示树的节点,TreeBin是实际放在table数组中的,代表了这个红黑树的根。

2. 主要方法

    查看源码时发现构造方法中什么也没做,这里就不贴代码了。

  • put方法

    主要是调用putVal方法

    可以看到首先会调用key的hashcode方法获取key的hashcode方法,然后调用spread方法进行一次再散列。

   HASH_BITS值为0x7fffffff,二进制为31个1

        可以看出spread方法中将key的hash值保留高16位,然后与上HASH_BITS保证最终结果是正数。

        回到put方法,获取到最终hash值后,判断table是否为空,若为空则初始化table

    可以看到初始化table时,首先判断sizeCtl是否为负数,若为负数则说明有别的线程在进行初始化,调用yield方法释放时间片,若不小于0则使用CAS指令设置sizeCtl为-1,设置成功后初始化Node数组,默认大小为16,同时需要注意到设置sc的时候首先减去了n的四分之一,也就是我们熟悉的阀值0.75。

    回到put方法

    这里获取了table该位置上的元素,若为空则直接设置node值

    继续往下看,这里使用了synchronized关键字来保证并发安全,还记得之前看JDK1.7版本的时候是使用segment继承ReentrantLock可重入锁来保证并发安全。

    根据代码逻辑,若当前Node数组位置上的元素的key与传入的key值相同且hash相同,则替换当前节点的value值,若不相同且当前节点是尾节点则根据传入的key和value创建一个node出来挂在链表的最尾部。

    部分1的代码逻辑就是若当前节点是树节点则做红黑树相关操作,这里限于能力就不细讲了。

    部分2的代码就是校验当前链表长度饰扣大于阀值,这里的阀值就是TREEIFY_THRESHOLD,也就是我们之前说的链表长度超过8的时候就转化为红黑树。

  • get方法

    可以看到get方法就简单了许多,还是调用spread方法进行再散列获取再散列后的hash值,然后根据我截图中的三种情况进行查找。

猜你喜欢

转载自blog.csdn.net/u011294519/article/details/89367711