什么是Java内存屏障?

面试题~

1 个回答

  • ketty
    不必太张扬,是花自然香。
    ### 背景 随着cpu由单核变成多核,又有了超线程。所以就会出现这样的问题,多核cpu在各自的缓存处理数据后,当同步数据到同一块主内存时,无法确定以谁的缓存数据为准。 所以为了解决cpu缓存一致性的问题,特地制定了一些操作协议,例如MSI、MOSI、Firefly等。而在这些操作协议下,对特定的内存或高速缓存进行读写访问的过程,就是内存模型。不同架构(ARM/X86等)的物理机有不同的内存模型。 ![image.png](https://images.codewd.com/f734e7e98129c6d9581d6bc3778fce35.png) Java为了一次编译、到处运行的跨平台性,使用class字节码作为中间代码,让不同架构的系统通过JVM来转换为对应的机器码。同样,为了屏蔽不同架构的机器上的内存访问差异,让Java程序在各种平台下都能达到一致的内存访问效果,所以Java制定了一套内存操作协议,即Java内存模型。 JMM(Java Memory Model)可以理解成一种协议,通过各种操作来定义对变量的读写、监视器的加锁和释放、线程启动和合并,保证多线程下变量的缓存一致性。 ![image.png](https://images.codewd.com/e0ee683bc7509aef91e35ac605d6313d.png) ### 工作内存和主内存 JMM主要目标是定义程序中各个变量的访问规则,即在JVM中将变量存储到内存和从内存中取出变量这样的底层细节,来实现缓存一致性。JMM在定义上将内存分为主内存和工作内存,主内存对应Heap,属于线程公有,工作内存对应虚拟机栈,属于每个线程私有。 同时JMM对内存中的变量做了以下规定: 1.规定所有的变量都存储在主内存中,线程不能直接读写主内存中的变量 2.每个线程有自己的工作内存,对变量的读取、赋值等必须在工作内存中进行 3.线程之间值的传递都需要通过主内存来完成。工作内存无法互相访问 4.工作内存保存了线程用到的变量的主内存副本拷贝 ### 原子操作 JMM规定了变量在主内存和工作内存中如何传输,同时也提供了八个原子指令来具体实现细节。 ![image.png](https://images.codewd.com/a10dc496a26d58cf5ad16ccf5a8a2ecc.png) 对于上面八个原子操作的使用,JMM制定了一些规则: read/load、store/write必须成对出现 不允许线程丢弃最近的assign操作,即工作内存中变量修改之后必须同步到主内存 不允许线程无原因地(没有assign操作)将变量工作内存同步到主内存 变量只能在主内存中诞生,并且必须在工作内存中初始化才能使用。即use、store之前必须经过load和assign 一个变量在同一时刻只能被一个线程lock。但一个线程可以lock多次。几次lock,只有对应次数的unlock变量才能解锁 一个变量被lock后,会清空所有工作内存中此变量的值。再次使用需要重新load、assign来初始化 一个变量未被lock,则不允许对它执行unlock,也不允许unlock其他线程lock的变量 一个变量unlock前,必须先把此变量同步到主内存中(store、write) 如何理解这八条规则和八条指令? lock/unlock指令让一个变量被一个线程独享,其他六个指令都是变量在工作内存中的赋值和传输操作。JMM的出现是为了让一个线程对变量修改时,其他线程停止修改,并能获取最新值,从而保证数据的一致性。 而lock可以锁定变量,让变量只能被一个线程修改,且其他线程工作内存中的此变量的值会失效,必须通过指令来获取最新的值。 ### JMM特性 原子性:lock和unlock提供了大范围的原子性,其他操作都具有原子性 可见性:当一个线程修改了变量的值,其他线程能立即得知修改 有序性:在本线程内操作都是有序的,相对于多个线程是无序的 ### 场景分析 synchronize可以看做是lock/unlock的实现,底层是由monitorenter/monitorexit来实现的。 测试代码如下: ```java public void test() { synchronized (this.getClass()) { System.out.println("Hello World!"); } } ``` 字节码指令如下: ![image.png](https://images.codewd.com/926bfef56531a18ac84a1f919330ed26.png) 在synchronize的作用范围内,在同一时间内只能一个线程获取锁进行操作,其他线程工作内存中的变量会失效,当此线程修改完unlock的时候,其他线程会重新加载变量最新值来进行操作,从而保证数据的一致性。 volatile关键字也通过内存屏障实现了lock/unlock的功能.
    发布于 2022-12-26 00:03 •IP属地 云南
    举报
    评论
    收藏