深入解析 helpTransfer 方法:多线程协作中的哈希表扩容

news/2024/9/25 10:10:22/

在这里插入图片描述

文章目录

    • 什么是哈希表
    • 哈希表的问题:扩容
    • 扩容的挑战
    • 扩容的原理
    • `helpTransfer` 方法
      • 检查是否正在扩容
      • 生成扩容标记并检查条件
      • 判断是否需要更多线程帮助
      • 加入搬家工作
      • 返回新表或旧表

什么是哈希表

哈希表(HashMap)是一种常用的数据结构,它通过“键值对”的形式来存储数据。它的核心思想是:根据每个“键”(key)的特征,通过一种叫做哈希函数的计算,把这个键映射到一个位置(格子)上。这样,我们就能快速找到或存储对应的值(value)。

打个比方:假设你有一个大抽屉(哈希表),你要把很多标有标签的物品放进去。你根据物品的标签(键),使用一个“算法”决定这个物品应该放在哪个小格子(具体位置)。

哈希表的问题:扩容

随着我们不断往哈希表里添加数据,格子会逐渐装满,并且有时不同的键会被放到同一个格子里(称为“哈希冲突”)。为了避免这些问题,哈希表有一个机制,当装入的元素数量超过一定的阈值时,它会 扩容,即换一个更大的表,把所有的键值对重新整理放进去。

打个比方:当你的抽屉里东西太多,而且标记不清时,你决定换一个更大的抽屉,并把物品重新整理好。

扩容的挑战

扩容本质上是需要把所有数据从旧的哈希表(旧抽屉)搬到新的哈希表(新抽屉)里。这在单线程的情况下还算简单,但如果有多个线程(搬运工)同时在操作这个哈希表,问题就变复杂了。我们希望多个线程可以协同工作,把数据快速、正确地搬到新表里。

打个比方:你和几个朋友(多个线程)决定一起搬家。问题是,如果没有协调好,大家可能会重复搬运某个物品,甚至搞混了物品的摆放位置。

扩容的原理

  1. 创建一个新的、更大的哈希表:我们要有一个新的抽屉,来装更多的东西。
  2. 重新分配每个键值对的位置:根据新的哈希表(新的抽屉)的大小,重新计算每个键值对应该放到哪个格子里。
  3. 多个线程协同搬运:多个线程一起工作,把旧哈希表中的数据搬运到新的哈希表中,保证每个线程只搬运自己负责的部分。

helpTransfer 方法

java">final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {Node<K,V>[] nextTab; int sc;if (tab != null && (f instanceof ForwardingNode) &&(nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {int rs = resizeStamp(tab.length) << RESIZE_STAMP_SHIFT;while (nextTab == nextTable && table == tab &&(sc = sizeCtl) < 0) {if (sc == rs + MAX_RESIZERS || sc == rs + 1 ||transferIndex <= 0)break;if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {transfer(tab, nextTab);break;}}return nextTab;}return table;
}

helpTransfer 方法是 ConcurrentHashMap 中扩容相关的一个方法。

helpTransfer 方法的作用是:在哈希表扩容过程中,多个线程协同工作,把旧哈希表的数据搬运到新的哈希表中。

接下来,我们会解释这个方法的功能是如何实现的。

检查是否正在扩容

java">if (tab != null && (f instanceof ForwardingNode) &&(nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {

解释:这个方法首先检查,当前哈希表是否正在进行扩容。

  • tab != null:当前哈希表必须存在。
  • f instanceof ForwardingNode:当前节点 f 是否是 ForwardingNode,这个节点表明哈希表正在扩容。
  • nextTab != null:是否已经创建了新的哈希表(新抽屉)。

打个比方:这就像我们检查当前抽屉是不是已经在搬家,如果是,那我们可以决定是否加入搬家队伍。

生成扩容标记并检查条件

java">int rs = resizeStamp(tab.length) << RESIZE_STAMP_SHIFT;
while (nextTab == nextTable && table == tab &&(sc = sizeCtl) < 0) {

解释:

  • resizeStamp(tab.length):生成一个标记,用来标识这次扩容。
  • 进入 while 循环:检查当前扩容状态。
    • nextTab == nextTable && table == tab:确认新旧哈希表没有变化,扩容工作还在进行。
    • sizeCtl < 0:检查扩容是否真的还在进行。

打个比方:这一步是保证当前的搬家工作没有被打断,你可以加入搬运队伍。

判断是否需要更多线程帮助

java">if (sc == rs + MAX_RESIZERS || sc == rs + 1 ||transferIndex <= 0)
break;

解释:此处判断是否还需要更多线程来帮助搬运。

  • 如果正在帮忙的线程已经够多了(sc == rs + MAX_RESIZERS),或者扩容快完成了(sc == rs + 1),当前线程就不再加入。
  • 如果所有桶(格子)都已经搬完了(transferIndex <= 0),那也不需要继续搬了。

打个比方:这是在判断是否已经有足够多的朋友在帮忙搬家,或者工作快要结束。如果不需要更多人手,你就不必加入。

加入搬家工作

java">if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {transfer(tab, nextTab);break;
}

解释:

  • compareAndSwapInt 是一种原子操作,它会尝试增加 sizeCtl,表示当前线程加入搬运工作。
  • 如果成功加入,线程会调用 transfer 方法,开始实际搬运数据。

打个比方:这里就是实际的搬运操作。当你加入搬家队伍后,开始负责自己的一部分,把旧抽屉的东西搬到新抽屉里。

返回新表或旧表

java">return nextTab;

解释:如果搬运工作完成,返回新的哈希表 nextTab。如果还没有完成,则返回旧哈希表。

打个比方:搬完东西后,你可以开始使用新的抽屉。如果没有搬完,那还继续用旧的抽屉。

在这里插入图片描述


http://www.ppmy.cn/news/1530246.html

相关文章

鸿蒙【项目打包】- .hap 和 .app;(测试如何安装发的hap包)(应用上架流程)

#打包成.hap需要用到真机 原因是&#xff1a;只有用上了真机才能在项目中配置 自动签名 #步骤: ##第一步:选择真机->选择项目结构->点Sigining Configs(签名配置) ##第二步:勾选Automatically generate signature(自动签名)->点击ok ##第三步:点击构建->点击 …

如何开启MySQL的慢日志查询

1.慢查询日志是什么&#xff1f; MySQL的慢查询日志是MySQL提供的一种日志记录&#xff0c;它用来记录在MySQL中响应时间超过阈值的语句&#xff0c;具体指运行时间超过long_query_time值的SQL&#xff0c;则会被记录到慢查询日志中。 long_query_time的默认值为10&#xff0…

大数据可视化-三元图

三元图是一种用于表示三种变量之间关系的可视化工具&#xff0c;常用于化学、材料科学和地质学等领域。它的特点是将三个变量的比例关系在一个等边三角形中展示&#xff0c;使得每个点的位置代表三个变量的相对比例。 1. 结构 三个角分别表示三个变量的最大值&#xff08;通常…

Parallels Desktop 20(Mac虚拟机) v20.0.0 for Mac 最新破解版(支持M系列)

Parallels Desktop 20 for Mac 正式发布&#xff0c;完全支持 macOS Sequoia 和 Windows 11 24H2&#xff0c;并且在企业版中引入了全新的管理门户。 据介绍&#xff0c;新版本针对 Windows、macOS 和 Linux 虚拟机进行了大量更新&#xff0c;最大的亮点是全新推出的 Parallels…

K8S容器实例Pod安装curl-vim-telnet工具

在没有域名的情况下&#xff0c;有时候需要调试接口等需要此工具 安装curl、telnet、vim等 直接使用 apk add curlapk add vimapk add tennet

5个月的编程记录

不知不觉间&#xff0c;我已经在计算机这条道路上走过了五个月。从五个月前第一次翻开《C Primer Plus》开始&#xff0c;我便对编程产生了浓厚的兴趣。这门学科与我高中所学的死板知识截然不同&#xff0c;每个问题都有千变万化的解法&#xff0c;让我感到无比新鲜。 几天后&a…

通过pyenv local 3.6.1 这里设置了当前目录的python版本,通过pycharm基于这个版本创建一个虚拟环境

要在 PyCharm 中基于你通过 pyenv local 设置的 Python 版本创建虚拟环境&#xff0c;可以按照以下步骤进行操作&#xff1a; 步骤 1: 获取当前使用的 Python 路径 通过 pyenv 查找当前项目下的 Python 解释器路径&#xff0c;使用以下命令&#xff1a; pyenv which python …

LeetCode从入门到超凡(二)递归与分治算法

引言 大家好&#xff0c;我是GISer Liu&#x1f601;&#xff0c;一名热爱AI技术的GIS开发者。本系列文章是我跟随DataWhale 2024年9月学习赛的LeetCode学习总结文档&#xff1b;在算法设计中&#xff0c;递归和分治算法是两种非常重要的思想和方法。它们不仅在解决复杂问题时表…