为什么说 HashMap 是非线程安全的?

点击上方☝Java编程技术乐园,轻松关注!及时获取有趣有料的技术文章做一个积极的人编码、改bug、提升自己我有一个乐园,面向编程,春暖花开!0. HashMap简单说几…

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

点击上方☝Java编程技术乐园,轻松关注!

及时获取有趣有料的技术文章

做一个积极的人编码、改bug、提升自己

我有一个乐园,面向编程,春暖花开!

640?wx_fmt=png

0. HashMap 简单说几句

我们在学习 HashMap 的时候,都知道 HashMap 是非线程安全的,同时我们知道 HashTable 是线程安全的,因为里面的方法使用了 synchronized 进行同步。

但是 HashMap 为什么是非线程安全的呢?难道仅仅就是因为内部的方法没有 synchronized 关键字修饰吗?这篇文章主要来分析一下原因。

我们知道 HashMap 底层是一个 Entry 数组,当发生 hash 冲突的时候,HashMap 是采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入。

640?wx_fmt=jpeg

HashMap为什么线程不安全,多线程并发的时候在什么情况下可能出现问题?

Javadoc中关于hashmap的一段描述如下:

此实现不是同步的。如果多个线程同时访问一个哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须 保持外部同步。(结构上的修改是指添加或删除一个或多个映射关系的任何操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)这一般通过对自然封装该映射的对象进行同步操作来完成。如果使用 Collections.synchronizedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的非同步访问,如下所示:

Map map = Collections.synchronizedMap(new HashMap<>());

1. HashMap 在插入的时候

640?wx_fmt=jpeg

在Hashmap做put操作的时候会调用到以上的addEntry方法。

现在假如A线程和B线程同时对同一个数组位置调用addEntry,两个线程会同时得到现在的头结点,然后A写入新的头结点之后,B也写入新的头结点,那B的写入操作就会覆盖A的写入操作造成A的写入操作丢失。

2. HashMap 在扩容的时候

addEntry中当加入新的键值对后键值对总数量超过门限值的时候会调用一个resize操作,代码如下:

640?wx_fmt=jpeg

这个操作会新生成一个新的容量的数组,然后对原数组的所有键值对重新进行计算和写入新的数组,之后指向新生成的数组。

HashMap 有个扩容的操作,这个操作会新生成一个新的容量的数组,然后对原数组的所有键值对重新进行计算和写入新的数组,之后指向新生成的数组。

那么问题来了,当多个线程同时进来,检测到总数量超过门限值的时候就会同时调用 resize 操作,各自生成新的数组并 rehash 后赋给该 map 底层的数组,结果最终只有最后一个线程生成的新数组被赋给该 map 底层,其他线程的均会丢失。而且当某些线程已经完成赋值而其他线程刚开始的时候,就会用已经被赋值的table作为原始数组,这样也会有问题。

其他地方还有很多可能会出现线程安全问题,我就不一一列举了,总之 HashMap 是非线程安全的,有并发问题时,建议使用 ConcrrentHashMap。

                                    

640?wx_fmt=gif

640?wx_fmt=png

640?wx_fmt=png欢迎长按下图关注公众号640?wx_fmt=png

640?wx_fmt=jpeg

后台回复【资源】,获取珍藏干货!

99.9%的伙伴都很喜欢smiley_63.png

往期精彩回顾

640?一文学会Java死锁和CPU 100% 问题的排查技巧

HashMap 用可变对象作为 key 踩坑

【面试】MySQL 中NULL和空值的区别?

Mybatis 批量插入引发的血案

Java内存管理-Stackoverflow问答-Java是传值还是传引用?(十一)

你有遇到过MySQL因大小写敏感导致的问题吗

MySQL 的COUNT(x)性能怎么样?

共勉:作为一名程序员你应该怎么提一个高质量的问题?

640?wx_fmt=png

  朕已阅 640?

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

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

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


相关推荐

  • mysql锁表和解锁语句_db2查看是否锁表

    mysql锁表和解锁语句_db2查看是否锁表介绍锁是计算机协调多个进程或线程并发访问某一资源的机制。锁保证数据并发访问的一致性、有效性;锁冲突也是影响数据库并发访问性能的一个重要因素。锁是Mysql在服务器层和存储引擎层的的并发控制。加锁是消耗资源的,锁的各种操作,包括获得锁、检测锁是否是否已解除、释放锁等。锁机制共享锁与排他锁共享锁(读锁):其他事务可以读,但不能写。排他锁(写锁):其他事务不能读取,也不能写。粒度锁MySQL不同的存储引擎支持不同的锁机制,所有的存储引擎都以自己的方式显现了锁机制,服务器层完全不了解存储引擎中的

    2022年8月23日
    12
  • java中clone_java copyproperties

    java中clone_java copyproperties1.克隆的用处在日常编码中我们经常需要产生某个对象的副本,这里的副本并不是指向同一个对象的不同引用,而是与当前对象状态一模一样的另一个新的对象。如果使用单纯的引用赋值,会发生什么效果呢?我们可以观察下面的代码:packagecom.coderap.foundation.clone;classAddress{publicStringprovince;publicStringcity;…

    2022年10月14日
    3
  • Push Button Web Hosting

    Push Button Web HostingPremiumPushButtonWordPressHostingPickaPlan&BuildYourSiteinUnder60Seconds!YourOneClickWordPressWebsiteBuilderWebWeaverEliteHostingMakesBuildingYour

    2022年10月8日
    4
  • MAC-npm更新版本「建议收藏」

    参考连接:参考连接运行下面的命令,查看npm可更新的版本:npm-goutdated输入一下命令,更新对应的版本:sudonpm-ginstallnpm@4.4.4在这里安装可能会报错:ENOTEMPTY:directorynotempty,rename’/usr/local/lib/node_modules/npm’->’/usr/local/lib/no

    2022年4月18日
    522
  • navicat连接mysql时出现2003(10060)错误

    navicat连接mysql时出现2003(10060)错误

    2021年6月16日
    179
  • 学术应用使用node-http-proxy集成谷歌学术「建议收藏」

    学术应用使用node-http-proxy集成谷歌学术「建议收藏」搞科研的同学肯定离不开谷歌学术,谷歌学术搜索是文献搜索下载一大利器。之前实验室开发了一款学术应用,遗留了历史问题,就是没有解决文献搜索的功能,而这个任务最后落在我的身上。我采用的方案就是集成谷歌学术,但是国内的网络环境,你懂的,自然状态下根本就访问不了谷歌学术的,你得翻墙才能访问。你不能期望使用你开发的学术应用都能翻墙访问谷歌学术(虽然搞科研的人电脑翻墙软件肯定都准备好了!),所以呢我还要给谷歌学术

    2022年8月31日
    7

发表回复

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

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