大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE稳定放心使用
HashMap为什么不是线程安全?
以JDK1.8的HashMap为例,引用作者: 一字马胡 所写文章中的一张图:
上图为HashMap的PUT方法的详细过程.其中造成线程不安全的方法主要是resize(扩容)方法.
情况一:
假设现在有线程A 和线程B 共同对同一个HashMap进行PU操作,假设A和B插入的Key-Value中key的hashcode是相同的,这说明该键值对将会插入到Table的同一个下标的,也就是会发生哈希碰撞,此时HashMap按照平时的做法是形成一个链表(若超过八个节点则是红黑树),现在我们插入的下标为null(Table[i]==null)则进行正常的插入,此时线程A进行到了这一步正准备插入,这时候线程A堵塞,线程B获得运行时间,进行同样操作,也是Table[i]==null , 此时它直接运行完整个PUT方法,成功将元素插入. 随后线程A获得运行时间接上上面的判断继续运行,进行了Table[i]==null的插入(此时其实应该是Table[i]!=null的操作,因为前面线程B已经插入了一个元素了),这样就会直接把原来线程B插入的数据直接覆盖了,如此一来就造成了线程不安全问题.
情况二:
这种情况是resize的时候造成的.现在假设HashMap中的Table情况如下:
线程A和线程B要对同一个HashMap进行PUT操作.插入后Table变为:
此时,线程A和B都需要对HashMap进行扩容.
假设线程A没有堵塞过,顺利完成resize后Table如下(这里的元素位置都是假设的):
如果线程B的resize是在Entry3的时候堵塞的,那么当它再次执行的时候就会造成处形成一个循环链表,当进行get操作时候可能陷入死循环,原因是:
线程B获得CPU时e = Entry3 ,next = Entry 2 ;正常赋值,然后进行下一次循环遍历时要注意,此时HashMap已经被线程A resize 过得了,那么就有 e = Entry 2 , next = Entry3 ; 头插法插入此时:,接着循环,e = Entry 3 ,next = Entry3.next = null (看图) ,此时再时候头插法就会形成循环链表了.,附上头插法代码:
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/189531.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...