美文网首页JAVA
为什么HashMap不是线程安全的操作?

为什么HashMap不是线程安全的操作?

作者: chengcongyue | 来源:发表于2019-04-10 15:11 被阅读2次

1,线程不安全的情况1

扩容时引起的死循环
我们知道在扩容的过程,大概就是将一个旧的位置的map移动到一个新的位置去,这样的话,在进行新位置的链表插入时,会使用尾插法,这样的话,我们一定先要记录下一个位置的node,即next=e.next
next表示当前结点的下一个结点,用next来保存,如下图所示

图片1
上图所示,当前遍历到key:0时,用e指向它,然后用next来保留它的下一个,即next指针指向key:4,注意这是(我们将操作当前的线程为A)我们A的挂起不在运行.
在多线程的环境下,又有了另一个线程我们称这个线程为B,B线程就完成了所有结点的调度,运行完为如下的情况
图片.png
此时此刻,线程A继续执行,它仍保留着上次的e和next这是e指向的是key:0
next指向的是key:4,这个时候线程A继续执行,而却线程A认为新表还是原来的表,所以它会进行新的一轮的更新,这个时候e指向的是key:0,所以,他会将key:0放在新表的第一个位置
图片.png ,执行完之后,此时e就变成了key:4,又因为是线程b以及完成了连接,导致key:4的next是key:0.
首先执行next=e.next,所以这个时候next指向的是key(0),然后将key:4放在Hash[i]的头部,然后这时候新的e就是key:0
图片.png
然后在执行,这个时候e就是key:0,我们将key:0在放在的链表头,即让key:0的next指向Key:4,这个时候问题就出现了,Key:4和Key:0就组成了闭环,这就是造成线程不安全的一个原因.
小结:总的来说就是因为线程B的扩容导致,原来key:0指向key:4的指针保留,然后又让key:4的next指向了key:0,当A进行扩容时,就会造成闭环

2.线程不安全的操作2

相对于扩容造成的闭环的线程不安全现象,当前的现象危害就小了很多,
这个问题比较好想象,比如有两个线程A和B,首先A希望插入一个key-value对到HashMap中,首先计算记录所要落到的桶的索引坐标,然后获取到该桶里面的链表头结点,此时线程A的时间片用完了,而此时线程B被调度得以执行,和线程A一样执行,只不过线程B成功将记录插到了桶里面,假设线程A插入的记录计算出来的桶索引和线程B要插入的记录计算出来的桶索引是一样的,那么当线程B成功插入之后,线程A再次被调度运行时,它依然持有过期的链表头但是它对此一无所知,以至于它认为它应该这样做,如此一来就覆盖了线程B插入的记录,这样线程B插入的记录就凭空消失了,造成了数据不一致的行为。

相关文章

网友评论

    本文标题:为什么HashMap不是线程安全的操作?

    本文链接:https://www.haomeiwen.com/subject/vhhniqtx.html