map 线程安全 java_HashMap 在 Java7 ,Java8 的线程安全问题

1.Java7 多线程 put

put -> 容量到达上限 -> 扩容(resize) -> transfer (转移旧散列表上的节点到新散列表)

在 transfer 这一步,因为Java7 使用了头插法,可能会导致某个线程的新散列表的某个槽成环

本质问题是 假如一个线程已经 transfer 完毕,因为使用头插法,会把链表逆置(图中原本的 A -> B , 被置为 B -> A)

如此一来,另外一个线程transfer 的时候,会保存一个错误的 A -> B 关系,把 A 当成当前节点 e,把 B 当成下一个节点 next。

但是现在实际的指向关系是 B -> A , 如此一来,e 和 next 先后是

A  B

B  A

A  null

因为使用头插法,在 B 还指向 A 的情况下,把 A 头插到 B 前面,成环,下次访问A或B会造成死循环,空耗CPU资源。

1e6f97367a141f92bfa556f0b0fa6344.png

2.Java 8 不再使用上述头插法,但是因为 没有 StoreLoad 屏障,在一般的 TSO CPU模型中,StoreBuffer中的内容无法被及时刷出,可能出现覆盖现象

假设有两个CPU核心,在跑两个线程,第一个CPU跑线程A,第二个CPU跑线程B

线程A 和 线程B 读取 散列数组的 i 位置 元素为空,所以都打算直接写入内容,线程A写入 m ,线程B写入 n

因为有缓存一致性协议,所以可以把缓存和内存看成一个统一的一致的存储系统

b1109831353a02b2a465f0324794b292.png

假设 线程 A 所在 CPU 先将 storeBuffer 的内容刷入 存储系统

4e376a2fd1a39e69db113c7d524f2e03.png

尔后,线程B 所在 CPU 也把 storeBuffer 的内容刷入存储系统

显然,线程A 的写入会被线程 B 的覆盖

167e1f32c346a2509751b18e2ec18d23.png

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值