这个问题是在面试时常问的几个问题,一般在问这个问题之前会问Hashmap和HashTable的区别?面试者一般会回答:hashtable是线程安全的,hashmap是线程不安全的。

那么面试官就会紧接着问道,为什么hashmap不是线程安全的,会造成什么问题么?于是面试者就回答:HashMap在并发情况下的put操作会造成死循环。

这时候就会被面试官问:HashMap在并发为什么造成死循环?

很多面试者这时候就会一脸懵。没有过相关经验和深入的理解源码是很难回答这个问题的。

下面我们就通过HahMap源码来验证下,多线程并发put操作为何会生成环形链表,产生死循环。

这是HashMap扩容的源码

/**
* Transfers all entries from current table to newTable.
*/
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) { while(null != e) {
//(关键代码)
Entry<K,V> next = e.next;
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} // while }
}

开始之前先回顾一下HashMap的扩容机制:

HashMap默认设定的装载因子为0.75(可改),HashMap的大小为length,已经装载的元素数量为num,当( num / length )> 装载因子时,

开始扩容

先创建一个散列表HashMap:Map<Integer> map = new HashMap<Integer>(2); ,装载因子默认0.75,当插入第二个元素时,会发生扩容

我们先在map中放入6、8两个元素。

这时有两个线程都执行put操作,那么在此刻两个线程都对HashMap进行扩容,这时候就注意在上文的源码里注释为(关键代码)这一行:Entry<K,V> next = e.next;

假如两个线程分别为A、B两个线程。A线程在执行到关键代码这一行线程就被挂起,那么此刻A线程中:e = 6; next = 8;

接着B线程开始进行扩容,假设新的散列表中,节点6 和 节点8 还是会产生散列冲突,那么线程B的扩容过程为:

  • 先申请一个空间为旧散列表两倍大的空间

  • 将节点6 迁移至新散列表

  • 将节点8 迁移至新散列表

此时线程B的扩容已经完成,节点8 的后继节点为节点6 ,节点6的后继节点为null。

我们将新旧两个散列表做个对比:

回顾一下线程A的当前状态:e = 6; next = 8;,处于挂起状态。接着A线程取消挂起状态,接着执行(关键代码)之后的代码:将e = 6;节点迁移至新的散列表,并将next = 8的节点赋值给e。扩容并迁移节点6后的状态,如下图所示:

于是第二次执行while循环时,当前待处理节点:e = 8;

在执行(关键代码)这一行时,由于线程B在扩容时将节点8的后继节点变为节点6,所以next不是为null,而是next = 6;

接着开始执行第三次while循环,由于节点6的后继节点为null,所以 next = null;,执行完第三次while循环的结果为:

循环结束。

可以看到扩容后的散列表中链表成环,如果这时候执行get()方法查询,就会导致死循环。

总结

HashMap的方法不是线程安全的。HashMap在并发执行put操作时发生扩容,可能会导致节点丢失,产生环形链表等情况。

  • 节点丢失,会导致数据不准
  • 生成环形链表,会导致get()方法死循环。

知识拓展

在jdk1.7中,由于扩容时使用头插法,在并发时可能会形成环状列表,导致死循环,在jdk1.8中改为尾插法,可以避免这种问题,但是依然避免不了节点丢失的问题。

建议

HashMap的设计初衷就不是在并发情况下使用,如果有并发的场景,推荐使用ConcurrentHashMap

面试官问:HashMap在并发情况下为什么造成死循环?一脸懵的更多相关文章

  1. 2020-04-22:谈谈JDK1.8下的HashMap在并发情况下链表成环的过程。(挖)

    福哥答案2020-04-22: jdk1.8下的hashmap采用的是尾插法,不会有链表成环的问题.jdk1.7下采用的头插***有链表成环的问题. hashmap成环原因的代码出现在transfer ...

  2. 面试官问我,Redis分布式锁如何续期?懵了。

    前言 上一篇[面试官问我,使用Dubbo有没有遇到一些坑?我笑了.]之后,又有一位粉丝和我说在面试过程中被虐了.鉴于这位粉丝是之前肥朝的粉丝,而且周一又要开启新一轮的面试,为了回馈他长期以来的支持,所 ...

  3. 面试官问线程安全的List,看完再也不怕了!

    最近在Java技术栈知识星球里面有球友问到了线程安全的 List: 扫码查看答案或加入知识星球 栈长在之前的文章<出场率比较高的一道多线程安全面试题>里面讲过 ArrayList 的不安全 ...

  4. 【原创】面试官问我G1回收器怎么知道你是什么时候的垃圾?

    这是why技术的第36篇原创文章 上面的图片是我上周末在家拍的.以后的文章里面我的第一张配图都用自己随手拍下的照片吧.分享生活,分享技术,哈哈. 阳台上的花开了,成都的春天快来了,疫情也应该快要过去了 ...

  5. [每日一题]面试官问:Async/Await 如何通过同步的方式实现异步?

    关注「松宝写代码」,精选好文,每日一题 ​时间永远是自己的 每分每秒也都是为自己的将来铺垫和增值 作者:saucxs | songEagle 一.前言 2020.12.23 日刚立的 flag,每日一 ...

  6. Java之HashMap在多线程情况下导致死循环的问题

    PS:不得不说Java编程思想这本书是真心强大.. 学习内容: 1.HashMap<K,V>在多线程的情况下出现的死循环现象   当初学Java的时候只是知道HashMap<K,V& ...

  7. 面试必问---HashMap原理分析

    一.HashMap的原理 众所周知,HashMap是用来存储Key-Value键值对的一种集合,这个键值对也叫做Entry,而每个Entry都是存储在数组当中,因此这个数组就是HashMap的主干.H ...

  8. 面试官问:JS的this指向

    前言 面试官出很多考题,基本都会变着方式来考察this指向,看候选人对JS基础知识是否扎实.读者可以先拉到底部看总结,再谷歌(或各技术平台)搜索几篇类似文章,看笔者写的文章和别人有什么不同(欢迎在评论 ...

  9. 当面试官问我ArrayList和LinkedList哪个更占空间时,我这么答让他眼前一亮

    前言 今天介绍一下Java的两个集合类,ArrayList和LinkedList,这两个集合的知识点几乎可以说面试必问的. 对于这两个集合类,相信大家都不陌生,ArrayList可以说是日常开发中用的 ...

随机推荐

  1. Cloudera Manager和CDH安装部署

    本次安装采用离线安装的方式,需要提前下载好需要的包. 1. 准备工作 1.1 环境说明 操作系统:RedHat企业级Linux6.5 64-bit Cloudera Manager:5.8.4 CDH ...

  2. Linux实战(14):Ubuntu修改root默认登陆

    第一步 首先登录系统,创建root用户的密码 在终端输入命令: sudo passwd root 然后输入设置的密码,这样就完成了设置root用户密码的步骤 第二步 修改文件 sudo nano /u ...

  3. Mysql实战(1):创建用户

    此文为个人实操汇总. 创建用户设置权限 create user 'user'@'%' identified by 'password'; #创建用户设置密码 grant all privileges ...

  4. LevelDb 101学习

    转自http://www.cnblogs.com/haippy/archive/2011/12/04/2276064.html LevelDb日知录之一:LevelDb 101 说起LevelDb也许 ...

  5. element-ui upload上传文件并携带参数 使用formData对象

    需求:上传文件的时候,需要携带其他的参数 问题:使用upload上传文件时,必须使用formData对象,而其他的参数通过data获取的到的,formData和data是不能同时传输的 解决:获取到的 ...

  6. 使用kind搭建kubernetes

    使用kind搭建kubernetes 目录 使用kind搭建kubernetes kind架构 创建集群 将镜像加载到kind的node中 配置kind集群 配置多节点 多控制面 指定Kubernet ...

  7. 黑菜菌的JAVA学习笔记

    简介 本文是笔者对<JAVA编程思想>的学习笔记.以自己的思维理解来写下这篇文章,尽可能地简练,易懂.本文将随本人学习进度实时更新 对象导论 抽象过程 汇编语言是对底层机器码的抽象,而面向 ...

  8. Jboss未授权访问漏洞复现

    一.前言 漏洞原因:在低版本中,默认可以访问Jboss web控制台(http://127.0.0.1:8080/jmx-console),无需用户名和密码. 二.环境配置 使用docker搭建环境 ...

  9. idea模拟前端向后台请求数据输出响应结果

    tools-httpClient-Test restful web service 通过上述步骤之后出现如下图 1 表示通过哪种方式请求:2 请求数据的地址头部:3 请求数据的除了头部之后的地址:4, ...

  10. Java 实现常见内排序

    一.内排序 1.排序基本概念 (1)什么是排序? 排序指将一个数据元素集合或者序列 按照某种规则 重新排列成一个 有序的集合或者序列.分为内排序.外排序.排序算法的好坏直接影响程序的执行速度以及存储空 ...