HashMap为什么线程不安全?浅析高并发情况下的HashMap

在了解hashmap为什么不是线程安全的之前,要先了解hashmap的扩容机制,之前已做过分析(传送门在此

知道了hashmap的扩容机制,就正式说一下多线程环境中,hashmap会有什么样的问题(问题主要出在扩容时的ReHash操作),

假设:此时一个HashMap已经到达了临界点,需要进行扩容( ReSize() )的动作。这时又两个线程 A和B,同时要对这个HashMap进行了put操作:这里写图片描述
put动作执行完毕之后变成:
这里写图片描述

超过临界值,线程A和B各自进行ReSize()的操作,分别对hashmap进行了扩容。
注意此时还没有进行Rehash的过程,只是创建了一个长度是原hashMap数组的2倍新的Entry空数组
这里写图片描述

好,接下来线程A和B就要进行ReHash的动作了,回顾一下Rehash的源码:

这里写图片描述

再一次假设:
1、线程A畅通无阻的进行着ReHash
2、线程B遍历到了Entry3对象,执行完上面红框中的代码之后,线程被挂起了。此时在B的眼里:e = Entry3,next = Entry2
此时的状态如下(e和next代表线程B的引用):
e和next代表线程B的引用
接下来线程B开始执行他的ReHash操作(对于此时的B,***e = Entry3
next = Entry2***):
这里写图片描述

执行到上面红框这句时,刚刚假设了A对于Entry3的hash结果是3,所以B的hash结果也肯定是3,所以i=3。
继续执行,Entry3放入了线程B中数组下标为3的位置,且e指向了Entry2。此时:e = Entry2 ,next = Entry2
此时的状态:
这里写图片描述
然后再一次执行transfer代码:
这里写图片描述
此时:e = Entry2,next = Entry3
整体状态如下:
这里写图片描述

然后从红框位置继续执行代码,用头插法把Entry2插入到了线程B的数组的头结点,执行完毕之后的状态为:
这里写图片描述

接下来又进行了一次循环,执行了transfer代码:
这里写图片描述
又执行到这句时:e = Entry3 , next = Entry3.next = null
接下来执行这句时:
这里写图片描述
重点!桥黑板划重点了!!
newTable[i] = Entry2
e = Entry3
Entry2.next = Entry3
Entry3.next = Entry2

!!!!!!!!!!!链表出现了闭环!!!!!!
整体状态变成了下图:
这里写图片描述
当调用Get方法查找一个不存在的Key,而这个Key的Hash结果恰好等于3的时候,由于位置3带有环形链表,所以程序将会进入死循环
正因为形成了闭环,会导致程序进入死循环,所以才说hashmap是线程不安全的。

扫描二维码关注公众号,回复: 1902463 查看本文章

总结:

在多线程情况下,会导致hashmap出现链表闭环,一旦进入了闭环get数据,程序就会进入死循环,所以导致HashMap是非线程安全的。

致谢:小灰

猜你喜欢

转载自blog.csdn.net/v_axis/article/details/78604505