ConcurrentHashMap1.7

本文详细解析了ConcurrentHashMap1.7的设计思路和基本原理,包括多线程环境下HashMap的问题、ConcurrentHashMap的并发安全机制、构造函数中的参数计算以及put方法的实现,强调了其在并发安全性和性能上的优化。
摘要由CSDN通过智能技术生成

整体思路

我理解的ConcurrentHashMap1.7整体就三件事(建议复习时候看,如果第一次看跳过这三张图):
在这里插入图片描述
第一件事源码流程图:
在这里插入图片描述
第二件事:
在这里插入图片描述
第三件事:
在这里插入图片描述

回忆HashMap多线程问题

在说ConcurrentHashMap之前我们回忆一下我们常用的HashMap在多线程环境中会有什么问题:
当有多个线程同时操作hashmap的时候很可能造成循环链表从而导致在get的时候陷入死循环,而ConcurrentHashMap就是问了解决多线程下hashmap的问题。

ConcurrentHashMap的基本原理

不同与hashmap,ConcurrentHashMap的结果组成如下:
在这里插入图片描述
图一
下面我们从具体的代码开始慢慢揭开它的面纱

1. ConcurrentHashMap的构造器:

在这里插入图片描述
在这里插入图片描述

参数说明

首先我们观察一下传入的参数,前两个不多赘述,我们主要看比hashmap多出来的concurrencyLevel并行度,这个就是用来影响我么图1中所说的segment的数量的,不过这个数量也同initialCapacity参数一样并不是用户传入多少就是多少,而是取2的幂次且大于并且最接近用户传入的数值(例如,出入initialCapacity=15,而实际创建的为16)。

代码主体分析

803-807行主要是做一些参数必要判断。而第808-813就是我们说的对concurrencyLevel这一参数的计算及赋值因为ssize的初始值为1对其就行位运算保证其为2的幂次,直到符合是取2的幂次且大于并且最接近用户传入的数值这一要求(这里之所以要是2的幂次是为了再算我们的元素要存入哪个角标位的用&运算,具体看hashmap篇)
816-820就是为了计算c而c值的计算结果又影响cap的计算结构,所以816-823实际上再算的就是图1中所说的每个segment底下应该放几个hashtable,先不看代码,我们已知initialCapacity和segment的总个数(ssize)那么要计算每个segment底下应该有几个hashtable那就是用initialCapacity/ssize=c(每个segment下的hashtable数),但是我们如果c为小数那么我们应该向下取整还是向上取整呢,很显然我们应该向上取整,及只能让hashtable多,而不应该少,那么816-823实际做的就是这个,818为计算c,819-820为保证向上取整,我们看到821中cap有个最小值为2,822-823保证每个segment下的hashtable为2的幂次。之后就是初始化segment了。

segment的结构

在这里插入图片描述
从上可知segment的结构,前2个不说了同hashmap意思一致,第三个就是我们图1的hashtable。
回到ConcurrentHashMap的构造函数:

 Segment<K,V> s0 =
            new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
                             (HashEntry<K,V>[])new HashEntry[cap]);
        Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];
        UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
        this.segments = ss;

首先是new了一个segment的结构,这个s0其实就是个范本目的就是当后边在创建segment的时候不需要在计算cap等值了,然后创建了整个segment数组当然将范本存入了ss的0位置。
至此整个构造函数就完结了,它主要就是:

  • 计算了segment数组大小。
  • segment对象的参数(cap及segment底下的hashtable数量)
  • 并构造了segment数组

2. ConcurrentHashMap的put方法:

再说之前我们要补充一个unsafe方法:

UNSAFE.getObject (数组名, 偏移地址)

这个函数可以取到主存中的对应数组名称的数组值,主存和工作内存具体看JMM模型

put方法

1 public V put(K key, V value) {
   
2       Segment<K,V> s;
3        if (value == null)
4            throw new NullPointerException();
5       int hash = hash(key);
6       int j = (hash >>> segmentShift) & segmentMask;
7        if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
8            (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
9           s = ensureSegment(j);
10      return s.put(key, hash, value, false);
11}

从代码3-4可知ConcurrentHashMap他不能有null的value,5-6行为计算key的hash并获得应该存入的segment下标j,之后的if操作就是判断segments数组对应的下标j中是否初始化了segment对象,我们先假设没有创建即走了s = ensureSegment(j);

ensureSegment

1	private Segment
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值