HashMap为什么线程不安全

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刷新主存?若是成员变量,可以一试!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值