HashMap多线程下发生死循环的原因

HashMap多线程下发生死循环的原因

大家好,又见面了,我是全栈君。

概述


大神陈皓已经在疫苗:JAVA HASHMAP的死循环一文中详细描述了HashMap多线程下产生死循环的原因,我仔细研读了这篇大作,做了一些笔记,加上自己的一些理解
整理出一些信息,发出来与大家交流交流。


HashMap存储的数据结构


陈皓在Hash表数据结构这一节提到了HashMap的数据结构以及扩容问题,关于这一点我之前写过的
HashMap的put和get方法原理HashMap扩容已经有详细的描述了。


多线程rehash的时候如何造成闭环链表


rehash源代码

这里写图片描述

这里写图片描述

正常的rehash过程


数据准备
在size=2的HashMap中按照顺序添加5, 7, 3这三个key,假设按照mod 2的算法来计算元素数组下标,那么key 5,7,3都会落在下标为1的数组桶中(发生hash冲突),如下图:

这里写图片描述

把HashMap的size扩容为4后,rehash的过程

注意,发生hash冲突的5,7,3虽然都是在同一个链表中,但是每个元素都得走rehash的过程,因为HashMap扩容后,这几个元素就未必都是在同一个链表中了

1、第一个是处理3这个key,先把key为3这个元素的next设置为空,并计算它在新数组中的下标,并存到新下标对应桶中,如下图:

这里写图片描述

2、第二个是处理7这个key,按照上面的约定,在新数组中3和7这个两个key还是发生了hash冲突,那么按照HashMap发生冲突的处理代码,链表的第一个元素存储的是最新插入的7,然后next指向3,如下图:

这里写图片描述

3、第三个是处理5这个key,如下图:

这里写图片描述

到这里一次正常rehash过程走完了,最后三个key的存储情况如下图:

这里写图片描述

并发下的rehash过程


当两个并发线程thread1和thread2都同时进入到transfer时,也即是,刚好thread1和thread2都要对HashMap进行扩容,万一这个时候thread1执行下面的代码时,被线程调度器挂起了,而thread2则正常的把扩容的操作做完,如下图:

这里写图片描述

那这个时候,容器的数据存储情况如下:

对于thread1

这里写图片描述

对于thread2

这里写图片描述

这个时候,thread1拥有执行权限了,则继续它的扩容操作,等thread1扩容完后就产生了一个环形链表了(注意这里省略了一些步骤,不太明白的,则可以看我之前写的HashMap的put和get方法原理HashMap扩容)

这里写图片描述

这个时候,如果有个get请求,就有可能发生死循环,一直在链表中绕来绕去的,没法终止。


原文链接


HashMap多线程下发生死循环的原因

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/108167.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • DBSCAN聚类︱scikit-learn中一种基于密度的聚类方式

    DBSCAN聚类︱scikit-learn中一种基于密度的聚类方式一 DBSCAN 聚类概述基于密度的方法的特点是不依赖于距离 而是依赖于密度 从而克服基于距离的算法只能发现 球形 聚簇的缺点 DBSCAN 的核心思想是从某个核心点出发 不断向密度可达的区域扩张 从而得到一个包含核心点和边界点的最大化区域 区域中任意两点密度相连 1 伪代码算法 DBSCAN 输入 E 半径 MinPts 给定点在 E 领域内成为核心对象的

    2026年2月4日
    1
  • java map 缓存_缓存用于

    java map 缓存_缓存用于缓存什么是缓存?平常的开发项目中,多多少少都会使用到缓存,因为一些数据我们没有必要每次查询的时候都去查询到数据库。缓存的使用场景:在Java应用中,对于访问频率高,更新少的数据,通常的方案是将这类数据加入缓存中,相对从数据库中读取,读缓存效率会有很大提升。在集群环境下,常用的分布式缓存有Redis等。但在某些业务场景上,可能不需要去搭建一套复杂的分布式缓存系统,在单机环境下,通常是会希望使用内部的缓存(LocalCache)。使用map缓存方案:基于ConcurrentHashMap实现数

    2022年9月27日
    4
  • vb中copymemory如何用_vb中lcase函数

    vb中copymemory如何用_vb中lcase函数vb中copymemory函数的使用挺耐人寻味的。copymemory的使用说明资料书上就一句“该函数用于将一块内存的数据从一个位置复制到另一个位置”。其参数数据类型destinationasany,sourceasany。尽管是any型可理解成任一类型但是我看很多地方都说参数是指针类型的。因此起初我很不解,既然是指针型的参数我们往往直接将变量传递过去而不是变量的地址传递过去不是非法的吗?

    2025年7月7日
    3
  • Http请求超时的一种处理方法[通俗易懂]

    Http请求超时的一种处理方法[通俗易懂]URLConnection类常见的超时处理就是调用其setConnectTimeout和setReadTimeout方法:setConnectTimeout:设置连接主机超时(单位:毫秒)setRea

    2022年8月2日
    9
  • a算法解决八数码实验报告_人工智能核心算法

    a算法解决八数码实验报告_人工智能核心算法实验一A*算法求解8数码问题一、实验目的熟悉和掌握启发式搜索的定义、估价函数和算法过程,并利用A*算法求解N数码难题,理解求解流程和搜索顺序。二、实验原理A*算法是一种启发式图搜索算法,其特点在于对估价函数的定义上。对于一般的启发式图搜索,总是选择估价函数f值最小的节点作为扩展节点。因此,f是根据需要找到一条最小代价路径的观点来估算节点的,所以,可考虑每个节点n的估价函数值为两个分量:从起始节点到节点n的实际代价g(n)以及从节点n到达目标节点的估价代价h(n),且hn≤h*n,h*n

    2025年6月21日
    4
  • 面试压力测试题情景题_压缩弹簧经常使用会发生什么

    面试压力测试题情景题_压缩弹簧经常使用会发生什么题解状态压缩dp,f[i][j]代表第i行状态为j的方案数#include<bits/stdc++.h>using namespace std;#define x first#define y second#define send string::npos#define lowbit(x) (x&(-x))#define left(x) x<<1#define right(x) x<<1|1#define transformu(s) tr..

    2022年8月8日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号