Java多线程下用HashMap要注意了
Java的HashMap是非线程安全的。多线程下应该用ConcurrentHashMap。
多线程下[HashMap]的问题(这里主要说死循环问题): 多线程put操作后,get操作导致死循环。 多线程put非NULL元素后,get操作得到NULL值。 多线程put操作,导致元素丢失。 1、为何出现死循环?(在多线程下使用非线程安全的HashMap,单线程根本不会出现)HashMap是采用链表解决Hash冲突,因为是链表结构,那么就很容易形成闭合的链路,这样在循环的时候只要有线程对这个HashMap进行get操作就会产生死循环。 在单线程情况下,只有一个线程对HashMap的数据结构进行操作,是不可能产生闭合的回路的。 那就只有在多线程并发的情况下才会出现这种情况,那就是在put操作的时候,如果 size>initialCapacity*loadFactor ,那么这时候HashMap就会进行rehash操作,随之HashMap的结构就会发生翻天覆地的变化。很有可能就是在两个线程在这个时候同时触发了rehash操作,产生了闭合的回路。 2、如何产生的:
存储数据 put() : public V put(K key, V value) { ...... //算Hash值 int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); //如果该key已被插入,则替换掉旧的value (链接操作) for (Entry e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; //该key不存在,需要增加一个结点 addEntry(hash, key, value, i); return null; }
当我们往HashMap中put元素的时候,先根据key的hash值得到这个元素在数组中的位置(即下标),然后就可以把这个元素放到对应的位置中了。
如果这个元素所在的位置上已经存放有其他元素了,那么在同一个位子上的元素将以链表的形式存放,新加入的元素放在链头,而先前加入的放在链尾。
检查容量是否超标addEntry: void addEntry(int hash, K key, V value, int bucketIndex) { Entry e = table[bucketIndex]; table[bucketIndex] = new Entry(hash, key, value, e); //查看当前的size是否超过了我们设定的阈值threshold,如果超过,需要resize if (size++ >= threshold) resize(2 * table.length); }
如果现在size已经超过了threshold,那么就要进行resize操作,新建一个更大尺寸的hash表,然后把数据从老的Hash表中迁移到新的Hash表中。
调整Hash表大小resize: void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity = oldTable.length; ...... //创建一个新的Hash Table Entry[] newTable = new Entry[newCapacity]; //将Old Hash Table上的数据迁移到New Hash Table上 transfer(newTable); table = newTable; threshold = (int)(newCapacity * loadFactor); }
当table[]数组容量较小,容易产生哈希碰撞,所以,Hash表的尺寸和容量非常的重要。
一般来说,Hash表这个容器当有数据要插入时,都会检查容量有没有超过设定的thredhold,如果超过,需要增大Hash表的尺寸,这个过程称为resize。
多个线程同时往HashMap添加新元素时,多次resize会有一定概率出现死循环,因为每次resize需要把旧的数据映射到新的哈希表,这一部分代码在 HashMap#transfer() 方法,如下: void transfer(Entry[] newTable) { Entry[] src = table; int newCapacity = newTable.length; //下面这段代码的意思是: // 从OldTable里摘一个元素出来,然后放到NewTable中 for (int j = 0; j < src.length; j++) { Entry e = src[j]; if (e != null) { src[j] = null; do { Entry next = e.next;//取出第一个元素 int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } while (e != null); } } }
标红代码是导致多线程使用hashmap出现CUP使用率骤增,出现死循环,从而多个线程阻塞的罪魁祸首。另外推荐:Java进阶视频资源 3、图解HashMap死循环:
正常的ReHash的过程(单线程):假设了我们的hash算法就是简单的用key mod 一下表的大小(也就是数组的长度)。
最上面的是old hash 表,其中的Hash表的size=2, 所以 key = 3, 7, 5 ,在mod 2以后都冲突在table[1] 这里了。接下来的三个步骤是Hash表 resize成4,然后所有的 重新rehash的过程。
并发下的Rehash(多线程)
1)假设我们有两个线程。 do { Entry next = e.next; // <--假设线程一执行到这里就被调度挂起了,执行其他操作 int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } while (e != null);
而我们的线程二执行完成了。于是我们有下面的这个样子:
注意,因为Thread1的 e 指向了key(3),而next指向了key(7),其在线程二rehash后,指向了线程二重组后的链表。我们可以看到链表的顺序被反转后。在这里线程一变成了操作经过线程二操作后的HashMap。
2)线程一被调度回来执行。 先是执行 newTalbe[i] = e; 然后是 e = next ,导致了e指向了key(7) , 而下一次循环的 next = e.next 导致了next指向了key(3) 。
3)一切安好。
线程一接着工作。把 key(7) 摘下来,放到newTable[i] 的第一个,然后把e和next往下移。这个元素所在的位置上已经存放有其他元素了,那么在同一个位子上的元素将以链表的形式存放,新加入的放在链头,而先前加入的放在链尾。
4)环形链接出现。
e.next = newTable[i] 导致 key(3).next 指向了 key(7) 。
注意:此时的 key(7).next 已经指向了 key(3) , 环形链表就这样出现了。
于是,当我们的线程一调用到, HashTable.get(11) 时,悲剧就出现了——Infinite Loop。
这里介绍了在多线程下为什么HashMap会出现死循环,不过在真实的生产环境下,不会使用线程不安全的HashMap的。
来源:https://juejin.cn/post/7003321088386170893
英雄联盟AP飞机和AP卡莎的争论,一个套路的强度是比较优势套路,是一种以经验为基础,低风险的实用主义的东西。。套路,在这个竞技游戏里屡见不鲜。但越是名目众多,就越是鱼龙混杂。有独树一帜的完整的思路,有拾人牙慧的换皮模板,也有滥竽充数的残次
英雄联盟官方分享双城之战芬恩概念设计稿双城之战芬恩的概念设计稿,由Riot资深概念设计师VictorMaury分享!国内领先的电竞赋能综合服务平台成立于2017年07月01日,是国内优质的电竞新营销解决方案服务商通过品
荒野大镖客2每日任务攻略更新2022年1月5日常规任务1在自由模式活动中用弓箭杀死的玩家03左上方发出的活动邀请加入一次用弓箭杀死3次玩家即可2在决战中用手枪杀死的玩家03快速加入一次占领枪战等系列赛用手枪(左轮不算)杀死3次
CSGO为什么没有保留子弹购买?纯粹因为技术限制和设计残留,没那么复杂。steam游戏SteamCSGO游戏1。6是HL引擎,子弹打在人身上。比如你买了一件武器和一些弹药,然后扔在地上,地上那把枪是只有弹匣内有子
原神常夜灵庙解密攻略原神常夜灵庙是稻妻渊下宫中需要解密的玩法,难度还是比较高的,因此很多玩家想知道原神常夜灵庙怎么解密?小编为您带来原神常夜灵庙解密攻略详解。原神常夜灵庙解密攻略1首先跑到地方,跟随指
万国觉醒丨玉经琢磨多成器,历经万苦终称雄262惊鸿如果在胜利前却步往往只会拥抱失败如果在困难时坚持常常会获得新的成功万国世界里有无数的挑战和机遇想要在王国中巍峨屹立就不得不付出更超乎常人的毅力今天阿醒要为大家讲述在万国大陆上262
熊猫人斗图装逼必备突然好想打死你,又有点怕坐牢熊猫人斗图必备熊猫人怼人表情表情你问我有没有空?那要看是什么事了熊猫人斗图必备熊猫人怼人表情表情你是超人,母猪你都敢日熊猫人斗图必备熊猫人怼人表情表情放
新赛季冰霜冲击大解析新赛季,很多装备都有了增强,今天聊一下,新版冰霜冲击兼具控制和抗爆发能力。名称冰霜冲击,增加240点物理防御增加1000点最大生命值,并且同时拥有一个主动技能和一个被动技能主动技能
这三款游戏都玩过?这四个理由都中了?实锤佛系青年无疑了这三款游戏都玩过?这四个理由都中了?实锤佛系青年无疑了佛系青年这个词最早在游戏行业出圈,还要追溯到2017年的旅行青蛙这款游戏,该游戏可操作性极低,游戏全程玩家不能控制自己的青蛙到
雷蛇发布全球首款模块化电竞桌概念设计ProjectSophiaIT之家1月6日消息,今日Razer雷蛇在CES2022发布了多款新产品。雷蛇带来了全球首款模块化电竞桌概念设计ProjectSophia。电竞桌表面覆盖一体式玻璃,桌子内放置多达
游戏还是战场?无人化战争时代或可在游戏中预演自工业革命以来,世界海军力量在科技的发展中不断升级与跨越,未来的战争,更像是双方经济与科技实力的对决。当未来战争走向科技化之路,战争的形态又会迎来怎样的改变?12月15日,硬核海战
被小虫啃食的孩子一初识患者1个月前,门诊来了一对儿患者。分别是一个8个月大的小宝宝,和他的母亲。据宝妈描述,前段时间带孩子回农村老家住了一段时间,可能是由于水土不服所以母子俩先后出现了湿疹,由于症
那些从小在重男轻女家庭长大的女孩,该如何提高自己的核心价值感01那些姑娘的自卑从何而来心理学家胡慎之在在人际关系中成长一书里提过一个案例,有位女士从小在重男轻女家庭长大,父亲脾气暴躁,母亲整天焦虑,哥哥争强好胜。她从小在物质上虽从未受过委屈
英雄联盟中哪些台词让你忍不住喊出来?英雄联盟中每一个英雄都有自己独特的台词,不过相比于整体而言,有些英雄的台词让我们记忆深刻。哪些英雄的台词可以让我们跟他一起喊出来呢?艾欧尼亚昂扬不灭!这句话是在刀妹释放大招剑阵时候
永劫无间S1赛季三排上分阵容推荐吃鸡三排阵容分享永劫无间目前开启了S1赛季的排位玩法,很多玩家都已经开始冲分,而且冲到了很高的位置,很多玩家则不知道用什么阵容比较好吃鸡,三排还是比较吃配置的,下面就来跟随小编一起来看看永劫无间S
热血传奇你爆过最贵的装备是什么?卖了多少钱?03年,46区天煞。当时是个31级小法师,纵横道打了一条邪恶毒蛇,掉了把裁决,当时是准备去练级的,超了一点药还,站上去捡不起来,一个道士已经靠过来了,先给我刷了层绿漆,那个紧张啊,
昨天晚上手机在王者荣耀登陆界面亮屏6小时!对手机危害严重吗?你好,很高兴回答你的问题。首先你用的什么型号手机呢?这个主要是确定它的屏幕是OLED屏幕还是普通的LCD屏幕。如果是OLED屏幕的话,肯定对屏幕损坏比较大的,OLED屏幕亮的时间长
英雄联盟里盲僧RQQ和QRQ打出来的伤害一样吗?大家好,我是史小坑游戏的苦逼小编,每周二五都会为大家带来不一样观点的游戏资讯顺带打个自家游戏硬广,喜欢的朋友别忘了关注我们!!盲僧是很多玩家的信仰,这个英雄在不同玩家手里都能玩出不
dnf旭旭宝宝充了多少钱?据旭旭宝宝亲口所说已经突破了700万人民币,当然是全部花费,从一开始玩就算起的,并不是从直播的算的。对于这个数据,经常看宝哥的小伙伴应该深信不疑吧?就最近俩个月增幅一套15的恍惚1
能不能推荐一些以星际为背景的游戏?这里是喜爱游戏的小白广阔的宇宙,充满着神秘,对于人类来说,在宇宙中探索是一件惬意和享受的事情。碍于现代科技的发展,目前我们还没有办法做到,但是作为游戏来说,广阔的星辰大海已经是我们
坦克世界为什么不火?坦克世界这个游戏只能说在中国火不起来,其他国家相对于我们来说要热情的多。玩了五六年我认为不火的原因有以下几点1这个游戏对新手很不友好!坦克世界是一个很值得玩和推敲的游戏,因为坦克的
如何看待玩英雄联盟却不知道opgg的人?说真的本人从s2开始接触lol到现在也一直没有断过,你说的那个什么opgg真的是没听过我玩个游戏图个乐子,知道那么多干嘛呀,开心就好啦。非得要看国外数据才能找到优越感嘛?不知道op