HashMap为什么线程不安全
程序员文章站
2022-04-14 22:54:16
...
HashMap由数组、链表、红黑树组成。
在put操作的时候,会形成环,所以线程不安全。
JDK1.7中,HashMap在resize的时候采用头插法,而JDK1.8之后采用尾插(并且引入了红黑树,还未看源码,暂时不讨论)。
下面直接上JDK1.7的源码,扩容的时候:(其实很简单,你想想怎么把一个节点插入到链表头?)
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) {
while(null != e) {
//常规处理手段,先保存下一个要处理的点
Entry<K,V> next = e.next;
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
//i就是在扩容后的数组上的索引
int i = indexFor(e.hash, newCapacity);
//将e插入到新数组的头部,现在e在数组外面飘着,
//所以下一句要将它放回来
e.next = newTable[i];
newTable[i] = e;
//处理下一个节点
e = next;
}
}
}
如果看不懂源码,没有关系,只要明白jdk1.7 rehash的时候是头插法。所以我们举例如下:
确实扩容后数组长度应该是两倍,但这不是重点。
假设扩容后,这俩的hash值依然一样。
下面开始分析:
线程1到下面这一句后就让出了时间片
Entry<K,V> next = e.next;(e现在是3,next为7)
然后线程2把rehash做完了。形成了上图的情况。也就是7-->3。
然后线程1继续工作,将3放到表头,到这里,问题就出现了,3-->7,而7-->3。
3的next是7。接下来不用说,线程1的操作怕是无穷尽了。
在别的博客看到这样一个问题,还有点意思,乍看有些道理:
其实e是局部变量,在虚拟机栈上,本身就是线程私有的,压根就不是线程共享的,哪来的volatile刷新主存?若是成员变量,可以一试!
上一篇: ASP.NET网站开发——安全验证
下一篇: 为什么HashMap线程不安全