在了解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的引用):
接下来线程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是线程不安全的。
总结:
在多线程情况下,会导致hashmap出现链表闭环,一旦进入了闭环get数据,程序就会进入死循环,所以导致HashMap是非线程安全的。
致谢:小灰