画出ConcurrentHashMap 1.8的put流程图,记住CAS和synchronized的结合

news/2025/3/23 15:06:40/

ConcurrentHashMap 1.8 put 操作流程

结构:数组 + 链表/红黑树。
并发控制:CAS(无锁操作) + synchronized(锁住桶头部节点)。
改进:相比1.7的分段锁(Segment),1.8锁粒度更细,仅锁住单个桶。

流程图(文字描述)

START↓
输入 key 和 value↓
计算 hash 值:hash = spread(key.hashCode()) // 高低位异或,减少冲突↓
获取数组长度 n = tab.length,计算索引 i = (n - 1) & hash↓
获取桶位置 tab[i] 的头节点 f = tabAt(tab, i) // 通过Unsafe.getObjectVolatile获取↓
[条件分支] f == null ?是 → 使用 CAS 插入新节点↓casTabAt(tab, i, null, new Node(hash, key, value)) // CAS尝试将null替换为新节点↓[成功] → 结束[失败] → 返回循环重试否 → 检查 f 的状态↓[条件分支] f.hash == -1 ? // -1表示正在扩容或初始化是 → 协助扩容:helpTransfer(tab, f)否 → 进入同步逻辑↓synchronized (f) { // 锁住桶头节点↓再次检查 tab[i] == f // 确保锁住的节点未变↓[链表操作]如果 f 是链表节点:遍历链表,查找 key↓[存在] → 更新 value[不存在] → 尾部插入新节点↓检查链表长度 >= 8 ? → treeifyBin() // 转红黑树[红黑树操作]如果 f 是红黑树节点:putTreeVal() // 红黑树插入↓} // synchronized 结束↓
更新 size:addCount(1L, binCount) // CAS更新元素计数,可能触发扩容↓
END

流程图关键点说明

1.CAS 使用:

初始化桶:当桶为空(tab[i] == null),用 casTabAt(基于Unsafe的CAS操作)插入新节点,无锁高效。
更新size:addCount 用 CAS 更新总元素数,避免多线程竞争。
示例代码:


static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i, Node<K,V> c, Node<K,V> v) {return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
2.synchronized 使用:

当桶不为空,锁住头节点 f,确保同一桶的并发操作线程安全。
锁粒度细化:只锁当前桶,不影响其他桶。
示例代码:

synchronized (f) {if (tabAt(tab, i) == f) {// 链表或红黑树操作}
}
3.CAS + synchronized 结合:
  • CAS 用于无竞争场景(如空桶插入、计数更新),减少锁开销。
  • synchronized 用于有竞争场景(如链表追加、红黑树操作),保证一致性。
  • 这种组合避免了1.7的全段锁,提高并发性能。

完整put方法源码(精简版)

final V putVal(K key, V value, boolean onlyIfAbsent) {int hash = spread(key.hashCode());int binCount = 0;Node<K,V>[] tab = table;for (;;) {int n = tab.length, i;Node<K,V> f;if (tab == null || n == 0)tab = initTable(); // 初始化表else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))break; // CAS插入成功} else if (f.hash == -1) // 扩容中tab = helpTransfer(tab, f);else {V oldVal = null;synchronized (f) { // 锁住头节点if (tabAt(tab, i) == f) {if (f.hash >= 0) { // 链表binCount = 1;for (Node<K,V> e = f;; ++binCount) {if (e.hash == hash && e.key.equals(key)) {oldVal = e.val;if (!onlyIfAbsent) e.val = value;break;}Node<K,V> pred = e;if ((e = e.next) == null) {pred.next = new Node<K,V>(hash, key, value);break;}}} else { // 红黑树TreeNode<K,V> p = (TreeNode<K,V>)f;oldVal = p.putTreeVal(this, tab, hash, key, value);}}}if (binCount != 0) {if (binCount >= TREEIFY_THRESHOLD)treeifyBin(tab, i); // 转红黑树if (oldVal != null)return oldVal;break;}}}addCount(1L, binCount); // 更新计数return null;
}

面试记录要点

1.流程简述:

计算hash -> 检查桶 -> CAS插入(空桶)或synchronized操作(非空桶) -> 更新size。

2.CAS作用:

空桶插入:casTabAt。
计数更新:addCount。

3.synchronized作用:

锁桶头节点,保证链表/红黑树操作安全。

4.性能优化:

CAS减少锁使用,synchronized细化锁粒度。

5.画图提示:

画一个数组,标注桶状态(空/链表/红黑树)。
用箭头表示CAS(空桶)和synchronized(链表操作)分支。

手绘流程图建议

  1. 顶层:输入key/value,计算hash。
  2. 分支1:tab[i] == null -> CAS插入 -> 结束。
  3. 分支2:tab[i] != null
    - 子分支1:f.hash == -1 -> 协助扩容。 子分支2:f.hash >= 0 ->
    - synchronized锁头节点 -> 链表/红黑树操作。
  4. 底部:更新size,结束。

口述练习:

“put 时先算 hash,桶为空用 CAS 插入;不为空用 synchronized 锁头节点,链表操作或转红黑树,最后 CAS 更新 size。”

文章来源:https://blog.csdn.net/weixin_43999182/article/details/146402044
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ppmy.cn/news/1581005.html

相关文章

jmeter将返回的数据写入csv文件

举例说明&#xff0c;我需要接口返回体中的exampleid与todoid的数据信息&#xff08;使用边界提取器先将其提取&#xff09;&#xff0c;并将其写入csv文件进行保存 使用后置处理器BeanShell 脚本实例如下 import java.io.*;// 设置要写入的文件路径 String filePath "…

无人机智能控制系统未来技术发展分析

无人机智能控制系统未来技术发展分析 一、视觉SLAM实时建图算法优化 研发方向 轻量化神经网络架构设计 开发基于注意力机制的轻量级神经网络&#xff08;如MobileViT&#xff09;&#xff0c;通过量化压缩和知识蒸馏技术降低模型计算量&#xff0c;实现 30 fps 30\text{fps}…

leetcode热题100道——两数之和

给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案&#xff0c;并且你不能使用两次相同的元素。 你可以按任意顺序返回答案。 示例 1…

能源监控软件UI界面设计:平衡功能性与审美性的艺术

在当今社会&#xff0c;能源作为推动经济发展的重要基石&#xff0c;其高效管理和合理利用显得尤为重要。随着科技的进步&#xff0c;能源监控软件应运而生&#xff0c;成为连接能源使用者与管理者之间的桥梁。而软件的UI&#xff08;用户界面&#xff09;设计&#xff0c;作为…

Git Bisect 使用指南:高效定位引入 Bug 的提交

git bisect 是一个用来定位引入 bug 的提交的命令。通过二分查找的方式&#xff0c;它能帮助你找到哪一个提交导致了问题&#xff0c;特别是在提交历史较长的情况下非常有用。 使用步骤 初始化 bisect&#xff1a; 首先&#xff0c;使用 git bisect start 来开始查找。 标记已…

D-Wave专用量子计算机登顶Science 率先展示在真实场景中的量子优势(内附下载)

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 文丨浪味仙 排版丨浪味仙 行业动向&#xff1a;4200字丨16分钟阅读 摘要&#xff1a;加拿大专用量子计算机公司 D-Wave 在 Science 期刊发表了论文&#xff0c;题为《Beyond-Classical Compu…

第二:go 链接mysql 数据库

mac  mysql 安装 的步骤 mysql  安装 配制&#xff1a; https://juejin.cn/post/7454870544929472550 mac brew 如何安装mysql数据库 要在Mac上使用Homebrew安装MySQL数据库&#xff0c;请按照以下步骤操作&#xff1a;步骤 1: 安装Homebrew 如果你还没有安装Homebrew&a…

Redis hyperloglog学习

背景知识 【伯努利试验】&#xff1a; 【伯努利试验】是一个概率论中的概念&#xff0c;指在相同的条件下重复进行n次独立的试验&#xff0c;每次试验只有两种可能的结果&#xff0c;且这两种结果发生的概率是固定的 抛硬币作为伯努利试验&#xff1a;在抛硬币时&#xff0c;我…