ConcurrentHashMap1.8 面试准备总结

1. ConcurrentHashMap

数据结构依然是 数组+链表+红黑树
利用 volatile+CAS+synchronized 来保证线程安全
1.8 的锁粒度是桶,相比于 1.7 的分段锁,锁粒度更小,并发度更高。

2. 计算 size

将所有 size 变化写入一个变量和一个数组。
计算 size 时,将变量和数组中的非空元素都加起来。

全程 CAS

只在数组未初始化且没有线程争用时,将 size 变化写入变量。
否则,只将 size 变化写入数组中任一元素。

数组可扩容,长度上限为机器核心数,长度也恒为 2 的幂。

线程探针哈希

思想:将 size 的变化分散到多个变量中,来降低线程冲突。

源码解析参考:ConcurrentHashMap 1.8 计算 size 的方式

1.7 做法:两次不加锁将所有段的 size 加起来。两次结果不同,加锁后再加一遍。

3. put 操作

不允许 key 或 value 为 null。

如果桶为空,CAS 自旋为桶添加第一个结点。
桶不为空,

  • 桶内第一个结点为 ForwardingNode,表示正在扩容,且当前桶已迁移完成,当前线程先去协助扩容,扩容完成后再执行 put 操作。
  • 桶内第一个结点不是 ForwardingNode,则对桶内第一个结点加 Synchronized 锁
    • 给 f 结点加锁后,还要判断 f 是否还为第一个结点,可能在加锁前,f 被 remove 操作移除。
    • 如果桶里是一颗红黑树,那么 f 是一个 TreeBin 对象,而不是红黑树的根结点。因为 put 会使根结点变化。所以对于红黑树锁住的是整个红黑树对象(TreeBin),而不是根结点(TreeNode)。

4. get 操作

不阻塞

如果定位到 ForwardingNode,调用其 find 方法到 nextTable 中的对应桶中查找。

如果在桶迁移的过程中,碰到 get 操作呢?
在桶迁移过程中,会把结点复制一份再迁移(链表中 lastRun 及其后面的结点除外),不会改变原桶内链表或红黑树的链接关系。所以只要桶内放的不是 ForwardingNode 结点,都可以正常访问。

引用三张很好的图:
普通链表如何迁移?
普通链表的迁移

红黑树如何迁移?
红黑树的迁移

hash 桶迁移中以及迁移后如何处理存取请求?
迁移中如何处理存取请求

5. remove 操作

也 Synchronized 桶内的第一个结点。

6. 数组初始化:initTable 操作

CAS 自旋 + 双重 check
保证数组只被一个线程初始化,且只被初始化一次。

源码逻辑:将 sizeCtl CAS 为 -1 获取初始化权,初始化前再检查一遍数组是否初始化。

小小感悟:这种双重 check 在并发编程中很常见。最近的例子是 put、remove、transfer 等操作,还有一个常见的例子是单例模式的双检锁写法。

7. transfer 扩容操作

sizeCtl 字段
为负数时,表示数组正在初始化或扩容,-1 表示正在初始化,-(1+活跃扩容线程数) 表示正在扩容。
否则,当 table 为 null 时,非零表示数组的初始化长度,为 0 表示使用默认初始长度 16。当 table 非空后,表示扩容阈值。
补充:正在扩容时,sizeCtl 的高 16 位为 table 的长度标记,用于其他线程判断扩容是否结束。低 16 位才是 活跃扩容线程数+1。

每个线程负责迁移的桶的数量与机器的 CPU 核心数有关,至少负责 16 个桶的迁移。

transferIndex

分段迁移,每个线程负责迁移一段连续的桶。

正在迁移时,Synchronized 锁住桶中第一个结点。

每个线程负责的段是等长的。
起始位置为 i = transferIndex-1;
终止位置为 bound = transferIndex-stride;
从右向左迁移 --i >= bound

桶迁移完成后,在桶中放入 ForwardingNode 结点。
最后一个扩容线程负责切换数组 nextTable = null; table = nextTab;

引图,多线程迁移:
多线程迁移

文中四张图片都引自:https://blog.csdn.net/zzu_seu/article/details/106698150

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值