欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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的时候是头插法。所以我们举例如下:

HashMap为什么线程不安全

 确实扩容后数组长度应该是两倍,但这不是重点。

假设扩容后,这俩的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的操作怕是无穷尽了。

 

在别的博客看到这样一个问题,还有点意思,乍看有些道理:

HashMap为什么线程不安全

其实e是局部变量,在虚拟机栈上,本身就是线程私有的,压根就不是线程共享的,哪来的volatile刷新主存?若是成员变量,可以一试!

相关标签: java hashmap