一直以来只是知道HashMap是线程不安全的,但是到底HashMap为什么线程不安全,多线程并发的时候在什么情况下可能出现问题?

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

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

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

   Map m = Collections.synchronizedMap(new HashMap(...));

1、

  1. void addEntry(int hash, K key, V value, int bucketIndex) {
  2. Entry e = table[bucketIndex];
  3. table[bucketIndex] = new Entry(hash, key, value, e);
  4. if (size++ >= threshold)
  5. resize(2 * table.length);
  6. }

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

2、

  1. final Entry removeEntryForKey(Object key) {
  2. int hash = (key == null) ? 0 : hash(key.hashCode());
  3. int i = indexFor(hash, table.length);
  4. Entry prev = table[i];
  5. Entry e = prev;
  6. while (e != null) {
  7. Entry next = e.next;
  8. Object k;
  9. if (e.hash == hash &&
  10. ((k = e.key) == key || (key != null && key.equals(k)))) {
  11. modCount++;
  12. size--;
  13. if (prev == e)
  14. table[i] = next;
  15. else
  16. prev.next = next;
  17. e.recordRemoval(this);
  18. return e;
  19. }
  20. prev = e;
  21. e = next;
  22. }
  23. return e;
  24. }

删除键值对的代码如上:

当多个线程同时操作同一个数组位置的时候,也都会先取得现在状态下该位置存储的头结点,然后各自去进行计算操作,之后再把结果写会到该数组位置去,其实写回的时候可能其他的线程已经就把这个位置给修改过了,就会覆盖其他线程的修改

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

  1. void resize(int newCapacity) {
  2. Entry[] oldTable = table;
  3. int oldCapacity = oldTable.length;
  4. if (oldCapacity == MAXIMUM_CAPACITY) {
  5. threshold = Integer.MAX_VALUE;
  6. return;
  7. }
  8. Entry[] newTable = new Entry[newCapacity];
  9. transfer(newTable);
  10. table = newTable;
  11. threshold = (int)(newCapacity * loadFactor);
  12. }

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

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

HashMap在什么场景下会由哪些内部方法导致线程不安全,至少给出一种场景的更多相关文章

  1. Android智能手机中各种音频场景下的audio data path

    上一篇文章(Android智能手机上的音频浅析)说本篇将详细讲解Android智能手机中各种音频场景下的音频数据流向,现在我们就开始.智能手机中音频的主要场景有音频播放.音频录制.语音通信等.不同场景 ...

  2. OC与Swift混编,三种场景的实现方式

    多语言并存时期,混编成为一种必须的方式 ,在多场影中实现OC和Swift语言的并存原来是如此简单 第一种场景,App中实现混编 创建桥接文件*.h 新建一个桥接文件,New File 选择 Heade ...

  3. 高性能场景下,HashMap的优化使用建议

    1. HashMap 在JDK 7 与 JDK8 下的差别 顺便理一下HashMap.get(Object key)的几个关键步骤,作为后面讨论的基础. 1.1 获取key的HashCode并二次加工 ...

  4. Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%。再往后,每提高0.1%,优化难度成指数级增长了。哪怕是千分之一,也直接影响用户体验,影响每天上万张机票的销售额。 在高并发场景下,提供了保证线程安全的对象、方法。比如经典的ConcurrentHashMap,它比起HashMap,有更小粒度的锁,并发读写性能更好。线程安全的StringBuilder取代S

    Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%.再往后,每提高0.1%,优化难度成指数级增长了.哪怕是千分之一,也直接影响用户体验,影响每天上万张机 ...

  5. HashMap在并发场景下踩过的坑

    本文来自网易云社区 作者:张伟 关于HashMap在并发场景下的问题有很多人,很多公司遇到过!也很多人总结过,我们很多时候都认为这样都坑距离自己很远,自己一定不会掉入这样都坑.可是我们随时都有就遇到了 ...

  6. 并发场景下HashMap死循环导致CPU100%的问题

    参考链接:并发场景下HashMap死循环导致CPU100%的问题

  7. java中HashMap在多线程环境下引起CPU100%的问题解决(转)

    最近项目中出现了Tomcat占用CPU100%的情况,原以为是代码中出现死循环,后台使用jstack做了dump,发现是系统中不合理使用HashMap导致出现了死循环(注意不是死锁). 产生这个死循环 ...

  8. 亿级流量场景下,大型架构设计实现【2】---storm篇

    承接之前的博:亿级流量场景下,大型缓存架构设计实现 续写本博客: ****************** start: 接下来,我们是要讲解商品详情页缓存架构,缓存预热和解决方案,缓存预热可能导致整个系 ...

  9. HBase指定大量列集合的场景下并发拉取数据时卡住的问题排查

    最近遇到一例,HBase 指定大量列集合的场景下,并发拉取数据,应用卡住不响应的情形.记录一下. 问题背景 退款导出中,为了获取商品规格编码,需要从 HBase 表 T 里拉取对应的数据. T 对商品 ...

随机推荐

  1. json在线格式化校验

    推荐个在线工具箱,json在线格式化转换编码,挺好用的 https://www.codejson.com/

  2. UVA571Jugs题解--简单数论(其实是瞎搞)

    题目链接 https://cn.vjudge.net/problem/UVA-571 分析 刚做了道倒水问题的题想看看能不能水二倍经验,结果发现了这道题 题意翻译:https://www.cnblog ...

  3. 洛谷UVA11987Almost Union-Find题解--并查集的删除

    题目链接 https://www.luogu.org/problemnew/show/UVA11987 分析 分析下操作发现就是加了个删除操作的并查集,怎么做删除操作呢. 我们用一个\(id[]\)记 ...

  4. Intellij Idea 建立maven项目 报错 :java: 错误: 不支持发行版本 5

    百度一搜这个错误,好多人都遇到了 不计其数的人都遇到.网上大多数都是菜鸟的愚见.经过本人的测试发现,用Idea建立普通的Java项目 然后随便建立一个类运行就不会报错. 但是如果用Idea建立一个普通 ...

  5. 转:Git和Github简单教程

    转自:https://www.cnblogs.com/schaepher/p/5561193.html Git和Github简单教程   原文链接:Git和Github简单教程 网络上关于Git和Gi ...

  6. eclipse中svn从分支合并到主干及冲突解决

    https://blog.csdn.net/shengqianfeng/article/details/79203156

  7. loadrunner执行场景时报Error -27040: Data Format Extension: Init: Internal error问题解决

    [问题描述] 在loadrunner控制台执行场景时,所有用户均Failed,查看errors,错误原因如下: Error -27040: Data Format Extension: Init: I ...

  8. Redis05——Redis高级运用(管道连接,发布订阅,布隆过滤器)

    Redis高级运用 一.管道连接redis(一次发送多个命令,节省往返时间) 1.安装nc yum install nc -y 2.通过nc连接redis nc localhost 6379 3.通过 ...

  9. Java语言基础(11)

    1 构造方法 构造方法是一种特殊的方法,只有在创建对象的时候才被调用,用来执行初始化的操作,比如给属性赋值... 1) 构造方法名字跟类名一致,没有返回值也就没有返回值类型 2) 格式: 类名(参数列 ...

  10. 【NOIP2012】同余方程

    原题: 求关于xx的同余方程ax≡1(mod b)的最小正整数解. 裸题 当年被这题劝退,现在老子终于学会exgcd了哈哈哈哈哈哈哈哈 ax≡1(mod b) => ax=1+by => ...