侧边栏壁纸
博主头像
BinArTale's Blog 博主等级

行动起来,活在当下

  • 累计撰写 6 篇文章
  • 累计创建 21 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

ConcurrentHashMap

binbin
2025-04-04 / 0 评论 / 0 点赞 / 9 阅读 / 0 字

1. 锁的不同

  1. 在 1.7中,ConcurrentHashMap 用的分段锁,他在初始化的时候,就已经固定segment(分段)的数量,一般固定是16segment ,这也就决定了ConcurrentHashMap 的并发能力是固定的,扩容也不会改变,无法根据需要再动态变化。写入时,首先通过key的hash判断segment下标,然后将这个segment上锁,然后再次通过key的hash得到具体的HashEntry下标。

  2. 在1.8中,采用的是CAS和synchronized作为锁,锁的粒度变得更细了,他是对每个链表的node或者红黑树的根节点(当链表长度大于8,且数组长度大于64时,链表会红黑树化,以提高查询速率)上锁,这样的好处就是,并发度提高了,数组扩容,并发度也会随着变高。写入时,首先判断计算key的hash,然后判断改值对应的node是否是空,如果是空则直接用CAS进行操作,如果非空,则通过synchronized 对该节点进行上锁,然后后续操作就一样了。

2. 扩容的区别

  1. 在 1.7中,是基于 Segment 进行扩容的,每个 segment 都维护自己的负载因子,当某个segment达到扩容阈值的时候,会单独进行扩容,不会影响其Segment

  2. 而 在 1.8 中就是进行全局扩容了:

  1. 扩容的触发:当表中元素超过阈值;单链表长度超过8要转为红黑树,但表容量不足64,优先扩容;插入、删除操作,发现桶被迁移(标记ForwardingNode),当前线程会显著迁移。

  2. 扩容过程:首先计算新数组的容量(旧数组的2倍),并创建;然后将就旧数组分为多个片,每个线程负责一个片的转移(当发现数组正在扩容是,会通helpTransfer() 方法加入迁移,然后通过CAS来竞争任务区间),然后通transferIndex来记录当前待迁移的桶区间;迁移的过程还分为链表迁移和红黑树迁移;

  3. 扩容期间的一些操作:当扩容时遇到读操作,且读到ForwardingNode 时,直接到新数组中进行查找,写操作的话如果没遇到ForwardingNode ,就直接插入。

3. size 逻辑的区别

  1. 1.7中,当调用size方法时不会加锁,而是先尝试获取三次不加锁的sum,如果三次总数一样,就说明数量没有变化,就直接返回了,如果不一样那就加锁再计算,其他线程也就没法访问了。

  2. 1.8 中就是直接计算返回结果的,他采LongAdder 的思想,

0

评论区