一直以来都知道HashMap是线程不安全的,但是到底为什么线程不安全,在多线程操作情况下什么时候线程不安全?

让我们先来了解一下HashMap的底层存储结构,HashMap底层是一个Entry数组,一旦发生Hash冲突的的时候,HashMap采用拉链法解决碰撞冲突,Entry内部的变量:

  1. final Object key;
  2. Object value;
  3. Entry next;
  4. int hash;

通过Entry内部的next变量可以知道使用的是链表,这时候我们可以知道,如果多个线程,在某一时刻同时操作HashMap并执行put操作,而有大于两个key的hash值相同,如图中a1、a2,这个时候需要解决碰撞冲突,而解决冲突的办法上面已经说过,对于链表的结构在这里不再赘述,暂且不讨论是从链表头部插入还是从尾部初入,这个时候两个线程如果恰好都取到了对应位置的头结点e1,而最终的结果可想而知,a1、a2两个数据中势必会有一个会丢失,如图所示:

再来看下put方法

  1. public Object put(Object obj, Object obj1)
  2. {
  3. if(table == EMPTY_TABLE)
  4. inflateTable(threshold);
  5. if(obj == null)
  6. return putForNullKey(obj1);
  7. int i = hash(obj);
  8. int j = indexFor(i, table.length);
  9. for(Entry entry = table[j]; entry != null; entry = entry.next)
  10. {
  11. Object obj2;
  12. if(entry.hash == i && ((obj2 = entry.key) == obj || obj.equals(obj2)))
  13. {
  14. Object obj3 = entry.value;
  15. entry.value = obj1;
  16. entry.recordAccess(this);
  17. return obj3;
  18. }
  19. }
  20. modCount++;
  21. addEntry(i, obj, obj1, j);
  22. return null;
  23. }

put方法不是同步的,同时调用了addEntry方法:

  1. void addEntry(int i, Object obj, Object obj1, int j)
  2. {
  3. if(size >= threshold && null != table[j])
  4. {
  5. resize(2 * table.length);
  6. i = null == obj ? 0 : hash(obj);
  7. j = indexFor(i, table.length);
  8. }
  9. createEntry(i, obj, obj1, j);
  10. }

addEntry方法依然不是同步的,所以导致了线程不安全出现伤处问题,其他类似操作不再说明,源码一看便知,下面主要说一下另一个非常重要的知识点,同样也是HashMap非线程安全的原因,我们知道在HashMap存在扩容的情况,对应的方法为HashMap中的resize方法:

  1. void resize(int i)
  2. {
  3. Entry aentry[] = table;
  4. int j = aentry.length;
  5. if(j == 1073741824)
  6. {
  7. threshold = 2147483647;
  8. return;
  9. } else
  10. {
  11. Entry aentry1[] = new Entry[i];
  12. transfer(aentry1, initHashSeedAsNeeded(i));
  13. table = aentry1;
  14. threshold = (int)Math.min((float)i * loadFactor, 1.073742E+009F);
  15. return;
  16. }
  17. }

可以看到扩容方法也不是同步的,通过代码我们知道在扩容过程中,会新生成一个新的容量的数组,然后对原数组的所有键值对重新进行计算和写入新的数组,之后指向新生成的数组。

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

HashMap为什么线程不安全(hash碰撞与扩容导致)的更多相关文章

  1. hashMap的线程不安全

    hashMap是非线程安全的,表现在两种情况下: 1 扩容: t1线程对map进行扩容,此时t2线程来读取数据,原本要读取位置为2的元素,扩容后此元素位置未必是2,则出现读取错误数据. 2 hash碰 ...

  2. HashMap之Hash碰撞源码解析

    转自:https://blog.csdn.net/luo_da/article/details/77507315 https://www.cnblogs.com/tongxuping/p/827619 ...

  3. HashMap之Hash碰撞冲突解决方案及未来改进

    说明:参考网上的两篇文章做了简单的总结,以备后查(http://blogread.cn/it/article/7191?f=wb  ,http://it.deepinmind.com/%E6%80%A ...

  4. 从头认识java-15.7 Map(4)-介绍HashMap的工作原理-hash碰撞(常常作为面试题)

    这一章节我们来讨论一下hash碰撞. 1.什么是hash碰撞? 就是两个对象的key的hashcode是一样的,这个时候怎么get他的value呢? 答案是通过equals遍历table那个位置上面的 ...

  5. Java基础知识强化之集合框架笔记80:HashMap的线程不安全性的体现

    1. HashMap 的线程不安全性的体现: 主要是下面两方面: (1)多线程环境下,多个线程同时resize()时候,容易产生死锁现象.即:resize死循环 (2)如果在使用迭代器的过程中有其他线 ...

  6. 面试官:小伙子,你给我说一下HashMap 为什么线程不安全?

    前言:我们都知道HashMap是线程不安全的,在多线程环境中不建议使用,但是其线程不安全主要体现在什么地方呢,本文将对该问题进行解密. 1.jdk1.7中的HashMap 在jdk1.8中对HashM ...

  7. HashMap 为什么线程不安全?

    作者:developer http://cnblogs.com/developer_chan/p/10450908.html 我们都知道HashMap是线程不安全的,在多线程环境中不建议使用,但是其线 ...

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

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

  9. Java并发基础08. 造成HashMap非线程安全的原因

    在前面我的一篇总结(6. 线程范围内共享数据)文章中提到,为了数据能在线程范围内使用,我用了 HashMap 来存储不同线程中的数据,key 为当前线程,value 为当前线程中的数据.我取的时候根据 ...

随机推荐

  1. .net项目中上传大图片失败

    .net项目中有时用户提出要上传大图片,一张图片有可能十几兆,本来用的第三方的上传控件,有限制图片上传大小的设置,以前设置的是2M.按照用户的要求,以为直接将限制图片上传大小的设置改下就可以了,但是当 ...

  2. Unity扩展让枚举视图中变成多选框

    如图: 定义属性描述特性(因为没有描述的数据,让绘制类去绘制所以为空) using UnityEngine; using System.Collections; public class EnumFl ...

  3. poj 3216 (最小路径覆盖)

    题意:有n个地方,m个任务,每个任务给出地点,开始的时间和完成需要的时间,问最少派多少工人去可以完成所有的任务.给出任意两点直接到达需要的时间,-1代表不能到达. 思路:很明显的最小路径覆盖问题,刚开 ...

  4. WAS下获取包路径下所有类

    最近做javaweb项目的混淆工作,用到proguard,该工具混淆.jar文件比较方便,故把所有项目代码和配置文件打成jar包, 生成的jar包经过proguard处理后,再次打包(解决progua ...

  5. [WARNING] Warning: selected war files include a WEB-INF/web.xml which will be ignored (webxml attribute is missing from war task, or ignoreWebxml attribute is specified as 'true')

    WARNING] Warning: selected war files include a WEB-INF/web.xml which will be ignored (webxml attribu ...

  6. 将mysql服务加入到系统服务中 服务器配置 注销时不会关闭mysql

    将mysql加入系统服务中: 或者在cmd中输入:mysql安装路径\mysql\bin\mydqld.exe --install mysql --defaults-file="mysql安 ...

  7. 伪元素::before和::after

    有时候我们的页面里面有不少其他网站的名字,而且还要求网站名后面还要有网站的链接,类似这样:百度(http://www.baidu.com).这个时候如果网站多的话写起来就很麻烦了 <a href ...

  8. PL/SQL分页查询

    create or replace procedure fenye(tabelname in varchar2,currentpage in number,pageSize in number,inW ...

  9. Mac系统杂项 (持续更新)

    一.调整LaunchPad的图标显示列数和行数 .调整每一列显示图标数量.在我的电脑上(1366 * 768),每列7个个人觉得比较不错 defaults write com.apple.dock s ...

  10. Remove Duplicate Letters

    316. Remove Duplicate Letters Total Accepted: 2367 Total Submissions: 12388 Difficulty: Medium Given ...