• 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资源。

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

    关于TSO内存模型:https://www.cnblogs.com/lqlqlq/p/13693876.html 

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

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

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

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

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

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

  • 相关阅读:
    JavaScript脚本的两种放置方式
    对象 属性 事件 方法
    媒体查询
    HTML5布局
    图像
    布局
    列表,表格,表单
    盒子
    vue.js常见面试题及常见命令介绍
    Winform读报工具
  • 原文地址:https://www.cnblogs.com/lqlqlq/p/13940088.html
Copyright © 2020-2023  润新知