ConcurrentHashMap源码分析

数组的容量必须是2的N次幂?

h&(n-1)计算下标位置
h:要放入的数
&:与 11得1,10得0,00得0
n:数组容量

ConcurrentHashMap 1.7

在这里插入图片描述
ConcurrnetHashMap 由很多个 Segment 组合,Segment 的个数一旦初始化就不能被改变,默认为16个,每一个Segment是类似于HashMap的结构,每一个hashMap内部可以进行扩容。

初始化

初始化 segments[0],默认大小为 2,负载因子 0.75,扩容阀值是 2*0.75=1.5,插入第二个值时才会进行扩容。

put

  1. 计算put的key的位置,获取指定位置的Segment。
  2. 如果Segment为空,初始化这个Segment。
  3. Segment.put 插入 key,value 值。首先计算put数据要放入的index位置,获取这个位置上的HashEntry,然后去遍历put,如果这个位置上的HashEntry不存在,判断一下当前容量有没有超过阈值,超过的话进行扩容,然后头插法插入。如果HashEntry存在,判断这个位置上的key和hash值是否一致,如果一致则替换这个值,不一致的话看看容量有没有超过阈值,超过的话扩容并且用头插法插入。

写的话保证线程安全:
Segment继承了ReentrantLock通过tryLock()获取锁,获取不到使用scanAndLockForPut方法继续获取。

扩容

ConcurrentHashMap 的扩容只会扩容到原来的两倍,老数组的数据要移动到新数组的时候,有两种情况,要么位置不变要么变为index + oldSize,因为他计算位置它是后几位跟1111去相与,比如原来的容量是16,我们只用看第五位是0还是1,如果是0那就是原来的位置加0就等于原来的位置,如果是1就是原来的位置+老的容量,扩容之后使用头插法插入到指定的位置。

两个 for 循环有疑惑,这里第一个 for 是为了寻找这样一个节点(lastRun),这个节点后面的所有 next 节点的新位置都是相同的。然后把这个作为一个链表赋值到新位置。第二个 for 循环是为了把剩余的元素通过头插法插入到指定位置链表。

get

  1. 计算得到 key 的存放位置。
  2. 遍历指定位置查找相同 key 的 value 值。

ConcurrentHashMap 1.8

Node 数组 + 链表 / 红黑树。当冲突链表达到一定长度时,链表会转换成红黑树。

初始化

自旋和 CAS 操作完成的。里面需要注意的是变量 sizeCtl ,它的值决定着当前的初始化状态。
-1 说明正在初始化
-N 说明有 N-1 个线程正在进行扩容
0 表示 table 初始化大小,如果 table 没有初始化
大于0 表示 table 扩容的阈值,如果 table 已经初始化。

put

  1. 根据key计算出hashcode
  2. 如果根据key定位的node为空利用CAS写入数据
  3. 如果不为空利用synchronized锁写入数据
  4. 如果hashcode==-1进行扩容,如果数组长度大于等于64会将链表转换成红黑树。

get

  1. 根据hash值计算位置
  2. 如果查找到指定位置,那么头结点就是要找的直接返回它的value
  3. 如果头结点hash小于0,说明正在扩容或者是红黑树,查找他
  4. 如果是链表,遍历查找他
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值