HashMap线程不安全的场景

7 篇文章 0 订阅

HashMap线程不安全的场景

下图为HashMap的部分注解,大体意思是:如果多个线程同时访问HashMap,并且至少有一个线程做了结构上的修改,那么它必须在外部进行同步。 (结构修改是添加或删除一个或多个映射的任何操作;仅仅修改键值不是结构修改。)

HashMap的部分注解
图片失效请点击

put

多个线程同时向Node[]的同一个位置插入时,会发生覆盖,只有一个线程的操作会被保留。

如下图源码,假如有A、B两个线程,同时在执行put且数组下标都为1,两个线程同时运行到第一个红框位置,判断tab[1]为null,A线程先执行tab[1]=NodeA,B线程再执行tab[1]=NodeB,这样B线程的操作就会覆盖A线程的。

第二个红框位置也会导致类似的问题,只不过是发生在链表上,而不是Node[]上

put的部分源码
图片失效请点击此处

resize + get

如果多个线程同时触发扩容resize,可能会形成环形链表,然后在调用get()获取一个不存在的元素时会发生死循环,导致CPU100%,JDK8已经解决了此问题。

主要原因就是,JDK7在Hash冲突时采用头插法,扩容时若旧链表的元素还会Hash到同一个新链表,那么新链表与旧链表的顺序是反的(从 A-> B变成了B -> A),在1.8后采用尾插法就不会出现这种问题,同时1.8的链表长度如果大于8就会转变成红黑树。

环形链表的形成
Java HashMap的死循环
JDK8的优化
HashMap全面分析及JDK8对HashMap的相关优化

remove

感觉Remove想法有错误,但是找不到问题在哪,欢迎指正不要误导更多的人。手动笑哭

多个线程同时remove会导致本来删除的元素重新回到链表上

假如有链表 A -> B -> C -> D,线程1删除B,线程2删除C,同时执行到红框出,导致以下情况,B.next = C且 B.next = D,发生了数据不一致,若线程2先执行线程1再执行的话,线程2:A -> B -> D ,线程1:A -> C,结果为A -> C不是 A -> D。
线程1:A.next = B.next = C
线程2:B.next = C.next = D

假如有链表 A -> B ,线程1删除B,线程2删除A,两个线程同时执行到红框处,然后如下执行
线程2:tab[i] = A.next = B
线程1:A.next = B.next = null
结果为 tab[i] = B;也就是说本来应该被删除的B还存在

remove的部分源码
图片失效请点击此处

size

由于++size--size不是原子性操作,所以也会导致数据不一致

参考文章

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值