Java ConcurrentHashMap

本文详细解析了ConcurrentHashMap的工作原理,包括初始化参数的选择、内部数据结构、扩容机制及多线程下的插入操作。通过理解这些核心内容,读者可以更好地掌握ConcurrentHashMap的使用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java 版本 jdk1.8

初始化
  • initialCapacity 根据自己需要在map中存放多少元素确定,默认DEFAULT_CAPACITY=16
  • loadFactor 扩容的负载因子,达到容量的多少比例之后开始考虑扩容,在java 8 版本基本只用于计算初始容量了
  • concurrencyLevel 估计有多少线程并发更新,会参考这个值确定map的容量,如果concurrencyLevel大于了initialCapacity 会以concurrency的值替换
  • sizeCtl 这个值才是map内部维护的值,用于控制map是否扩容,是根据以上三个值来确定的计算方式如下:cap = (long)(1.0 + (long)initialCapacity / loadFactor),会将cap取大于cap的最小2的N次幂,即 cap=10 最后sizeCtl =16242^424),不过这个值会在内部维护的Node数组初始化完成之后或者扩容完成之后被重置 sizeCtl = n-(n>>2)
  • table 主要数据保存在这个Node数组里面,创建一个sizeCtl大小的Node数组
  • nextTable 扩容之后的Node数组,当且仅当在扩容阶段的时候不为空
内部数据结构
  • Node 数据节点,table字段就是它的一个数组。内部维护next字段,当有多个元素的hash相等时,就形成链表
  • TreeNode 继承Node,树形节点,在一定情况下,Node链表会转换为红黑树。内部有一个red字段表明这个节点事红节点还是黑节点
  • TreeBin 继承Node,红黑树的根节点。不保存实际数据,hash=-2
  • ForwadinNode 迁移标记节点。标记此节点下的所有元素已经迁移到新的table中,内部保存了对扩容之后的Node数组的引用
put

ConcurrentHashMap主要是用于并发读写的,所以,在插入方法中考虑了多线程并发操作

  1. 计算key的hash,和HashMap一样都是将hashCode()的高16位与低16位做异或运算,以减小hash碰撞。有一点不一样的是,这里会将异或运算的结果再和0x7fffffff做与运算,把hash控制在0~231之间
  2. 插入
    1. 新元素index (hash & (n-1),n为当前table的长度)对应位置为空,则使用CAS更新,若此时对该位置有多个线程并发插入,肯定只有一个CAS操作会成功,其余的都会失败,失败的元素会不断的重试直到成功为止,不过当再一次重试时,对应位置已然不是空,所以会走下面的流程
    2. 当新元素index的位置已经存在元素了,那么先对已存在的这个元素身上添加一个监视器(锁),防止并发对这个节点进行更新,但是这里不用太担心锁对性能影响,因为按照初始容量来算,也只有1/16的机会你会遇到这个锁,扩容之后几率更小。将新的元素追加到这个元素后面,形成链表分支
    3. 如果新元素index位置存在的是TreeBin那么表明这个节点下已经形成了红黑树,按照红黑树的方式将新的节点插入就可以了,同样在插入之前要锁住index位置那个节点,防止并发
  3. 记录并检查数量。map中的元素个数是保存在baseCount字段中,有新的元素加入时,使用CAS 来更改baseCount的值,在多线程环境下,可能有多个线程在同时更改,造成失败。此时并没有选择重试,而是使用在内部的一个CounterCell数组来保存在CAS失败的计数,所以,当我们调用size方法时,其实得到的是baseCount + CounterCell[i].value得到的
  4. 迁移。ConcurrentHashMap的扩容采用多线程的方式进行数据迁移,在扩容阶段,所有对map元素个数产生影响的线程,都可以参与到扩容流程中。在扩容阶段,把整个Node数组分成N个stride,每个线程负责一个stride,迁移完成一个stride再去看是否还有需要迁移的stride,直到所有stride迁移完成

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值