hashMap线程不安全的原因及表现
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hashMap线程不安全的原因及表现相关的知识,希望对你有一定的参考价值。
参考技术A 1 扩容时可能造成死循环,扩容时会造成死锁,形成环形链表;或者造成扩容大小不一致等问题
2 多个线程put的时,get的值可能不一致,put的操作不是原子性的
3 删除键值对的时候,会删除刚刚修改的位置元素
扩容操作时:
这个操作会新生成一个新的容量的数组,然后对原数组的所有键值对重新进行计算和写入新的数组,之后指向新生成的数组。
当多个线程同时检测到总数量超过门限值的时候就会同时调用resize操作,各自生成新的数组并rehash后赋给该map底层的数组table,结果最终只有最后一个线程生成的新数组被赋给table变量,其他线程的均会丢失。而且当某些线程已经完成赋值而其他线程刚开始的时候,就会用已经被赋值的table作为原始数组,这样也会有问题。
环形链表的原因: 参考
大概看下transfer:
经过这几步,我们会发现转移的时候是逆序的。假如转移前链表顺序是1->2->3,那么转移后就会变成3->2->1。这时候就有点头绪了,死锁问题不就是因为1->2的同时2->1造成的吗?所以,HashMap 的死锁问题就出在这个 transfer() 函数上。
put时:
在hashmap做put操作的时候会调用到以上的方法。现在假如A线程和B线程同时对同一个数组位置调用addEntry,两个线程会同时得到现在的头结点,然后A写入新的头结点之后,B也写入新的头结点,那B的写入操作就会覆盖A的写入操作造成A的写入操作丢失
删除时: 参考
当多个线程同时操作同一个数组位置的时候,也都会先取得现在状态下该位置存储的头结点,然后各自去进行计算操作,之后再把结果写会到该数组位置去,其实写回的时候可能其他的线程已经就把这个位置给修改过了,就会覆盖其他线程的修改。
补充:
内部Entry数组默认大小是16,默认负载因子是0.75
内部Entry数组大小是2的幂
元素个数超过当前大小*默认因子的时候会扩容到当前大小的2倍
扩容是为了减少单个Entry数组链表的平均长度
HashMap线程不安全的主要因素的put过程中会发生扩容,多个线程会同时操作同一块内存导致
JDK7使用数组+链表方式实现;JDK8使用数组+链表/红黑树的方式实现:链表长度为8,链表转化为红黑树;红黑树节点个数为6,红黑树会转化为链表
HashMap为啥不安全?
有两个原因
存放数据时,HashMap不是线程安全的
比如有两个线程A和B,首先A希望插入一个key-value对到HashMap中,首先计算记录所要落到的桶的索引坐标,然后获取到该桶里面的链表头结点,此时线程A的时间片用完了,而此时线程B被调度得以执行,和线程A一样执行,只不过线程B成功将记录插到了桶里面,假设线程A插入的记录计算出来的桶索引和线程B要插入的记录计算出来的桶索引是一样的,那么当线程B成功插入之后,线程A再次被调度运行时,它依然持有过期的链表头但是它对此一无所知,以至于它认为它应该这样做,如此一来就覆盖了线程B插入的记录,这样线程B插入的记录就凭空消失了,造成了数据不一致的行为。
HashMap在容量不足的时候会进行resize,将自己扩容为原来的两倍,而reszie不是线程安全的
假设有两个线程同时需要执行resize操作,我们原来的桶数量为2,记录数为3,需要resize桶到4,原来的记录分别为:[3,A],[7,B],[5,C],在原来的map里面,我们发现这三个entry都落到了第二个桶里面。 假设线程thread1执行到了transfer方法的Entry next = e.next这一句,然后时间片用完了,此时的e = [3,A], next = [7,B]。线程thread2被调度执行并且顺利完成了resize操作,需要注意的是,此时的[7,B]的next为[3,A]。此时线程thread1重新被调度运行,此时的thread1持有的引用是已经被thread2 resize之后的结果。线程thread1首先将[3,A]迁移到新的数组上,然后再处理[7,B],而[7,B]被链接到了[3,A]的后面,处理完[7,B]之后,就需要处理[7,B]的next了啊,而通过thread2的resize之后,[7,B]的next变为了[3,A],此时,[3,A]和[7,B]形成了环形链表,在get的时候,如果get的key的桶索引和[3,A]和[7,B]一样,那么就会陷入死循环。
2.HashMap的实现 java7和java8在实现HashMap上有所区别,当然java8的效率要更好一些,主要是java8的HashMap在java7的基础上增加了红黑树这种数据结构,使得在桶里面查找数据的...
以上是关于hashMap线程不安全的原因及表现的主要内容,如果未能解决你的问题,请参考以下文章
HashMap在高并发下如果没有处理线程安全会有怎样的安全隐患,具体表现是什么