• JMM 内存模型概念


    JMM 内存模型概念

    我们知道 CPU 直接操作高速缓存寄存器,而不是直接操作主内存,那么有缓存的话,就会造成数据一致性问题,所以为了解决这些问题,提出了 JMM 规范。

    JMM 全称为 Java Memory Model,Java 虚拟机定义出一种逻辑规范或者说是抽象概念,用来屏蔽各种硬件和操作系统内存的访问差异,实现让 Java 程序在各种平台下都能达到一致的内存访问效果。换句话说就是通过这组规范定义了程序中对共享变量的读写能够互相可见,尤其是多线程情况下的读写操作,都是围绕着多线程的原子性、可见性、有序性展开的。

    对JMM 还应该要有认识的点,如下:

    • 不用 Java 虚拟机的内存区域划分,是两种逻辑的存在,是不同层次的划分结果,侧重点各有不同。

    • 主内存可以对应 Java 中的堆内存,工作内存可以对应栈中的部分。

    • 更底层次地来说,主内存对应硬件的物理内存,工作内存对应的是寄存器和高速缓存寄存器。

    JMM 操作内存示意图

    在这里插入图片描述
    从图中发现线程操作共享变量,都需把主内存中共享变量拷贝一份到自己的工作内存中,然后才可以进行操作,操作完之后又在写回主内存。如果考虑到高并发场景下,各个线程先把值在自己的工作内存中修改过后,假设有个线程先把值赋值回去了,此时主内存的值已经修改了,但是另一个线程还是拿着自己工作内存中的数据在操作,然后又给写回去了,明显这里第二个线程写回去的数据是有问题的,那么这里我们怎么可以保证,让一个线程修改主内存中的值,然后通知到其他线程,这里就涉及到了线程的可见性问题了,也是 JMM 三大特性之一,在了解线程可见性之前我们先看下 JMM 给我提供的8种原子操作来辅助各线程间内存间的交互?

    JMM 8个原子操作(内存间的交互操作)

    这八个原子操作类也是实现了线程可见性的基础步骤,如下:

    • lock: 锁定,把变量标识为线程独占,作用于主内存变量
    • unlock:解锁,把锁定的变量释放,其他线程才能使用,作用于主内存
    • read:读取,把变量值从主内存中读取到工作内存(只是读取到了工作内存,并没有指定到某个变量)
    • load:载入,把 read 读取到的变量放入工作内存中的副本变量中
    • use:使用,把工作内存中一个变量的值传递到执行引擎
    • assign:赋值,把从执行引擎接受到的值赋值到工作内存中的副本变量中
    • store:存储,把工作内存中的变量传递到主内存中(只是存储到了主内存,并没有指定到某个变量)
    • write:写入,把 store 存储进来的值写入到主内存中的共享变量

    下图展示了两个线程同时对主内存中的共享变量 flag 修改过程:
    在这里插入图片描述
    首先线程要从主内存中读取共享变量的值,那么首先就要给这个共享变量加锁(lock 操作),然后通过 read 操作,把 flag 的值从主内存读取到工作内存中(其实就是高速缓冲寄存器),然后再通过 load 操作,将工作内存中 flag 的值加载到变量副本中,然后通过 use 操作,将变量的值传递给 CPU 执行引擎。

    CPU 执行完之后,通过 assign 操作,将计算结果存储到变量副本中,接着通过 store 操作,将变量副本的 flag 值从工作内存传递给主内存,然后执行 write 操作,把 flag 的值重写写会到主内存中的共享变量 flag,最后通过 unlock 操作线程释放该锁,其他线程又可以对此变量加锁,重复上述流程。

    然后这里有几个内存间交互的规则需要注意下:

    • 不允许 read 和 load、store 和 write 操作单独出现,以上两个操作必须按照顺序执行,但不保证连续性,也就是说 read 和 load、store 和 write 之间是可以插入其他指令的
    • 不允许一个线程丢弃它最近的 assign 操作,即变量在工作内存中改变了之后必须把该变化同步会主内存
    • 不允许一个线程无原因的(没有任何的 assign 修改赋值操作)把数据从工作内存同步会主内存中年
    • 一个新的变量只能从主内存中诞生,不允许在工作内存中直接使用一个未初始化的变量,也就是对一个变量实施 user 和 store 操作之前,必须先执行过了 load 和 assign 操作
    • 一个变量在同一时刻只允许一个线程对其 lock 操作,但 lock 操作可以被同一个线程多次重复执行,多次 lock 后,只有执行相同次数的 unlock 操作,变量才会解锁
    • 如果对一个变量执行 lock 操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新 load
    • 如果一个变量没有被 lock 锁定,则不允许对它执行 unlock 操作,也不能 unlock 一个被其他线程锁定的变量
    • 对一个变量执行 unlock 之前,必须先把此变量同步会主内存(执行 store 和 write 操作)
  • 相关阅读:
    926. 将字符串翻转到单调递增-前缀和算法解决
    IVIEW Table/Div 自适应高度
    uni-app 微信小程序问题集锦
    Redis系列之初识
    刷题笔记28——一直分不清的Kruskal、Prim、Dijkstra算法
    React Native for Arcgis 地图开发 聚合图Cluster (十一)
    Python技法:实现简单的递归下降Parser
    接口测试常用技能:Jmeter操作数据库
    2022 退役记
    力扣 36. 有效的数独 C语言实现
  • 原文地址:https://blog.csdn.net/qq_35971258/article/details/125989913