多核并发缓存架构
Java 内存模型
Java 线程内存模型跟 CPU 缓存模型类似, 是基于 CPU 缓存模型来建立的, Java 线程内存模型是标准化的, 屏蔽掉了底层不同计算机的区别.
例子
编写代码来分析
- public class VolatileVisibilityTest {
- private static boolean initFlag = false;
- public static void main(String[] args) throws InterruptedException {
- new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.println("等待数据准备..");
- while (!initFlag){
- }
- System.out.println("============ 数据准备完毕, 执行程序逻辑");
- }
- }).start();
- Thread.sleep(2000);
- new Thread(new Runnable() {
- @Override
- public void run() {
- prepareData();
- }
- }).start();
- }
- public static void prepareData(){
- ![](https://img2018.cnblogs.com/blog/1330447/201907/1330447-20190710190530342-1378616179.png)
- System.out.println("数据准备中..");
- initFlag = true;
- System.out.println("数据准备完毕!");
- }
- }
执行程序, 打印结果
并未出现
============ 数据准备完毕, 执行程序逻辑
这段结果
分析
第一个线程给了 initFlag 为 false, 第二个执行了 prepareData() 所以 initFlag 为 true, 但是第一个线程中的 flag 还是为 false.
如果给 initFlag 加个 volatile 关键字:
public class VolatileVisibilityTest { private static volatile boolean initFlag = false; public static void main(String[] args) throws InterruptedException { new Thread(new Runnable() { @Override public void run() { System.out.println("等待数据准备.."); while (!initFlag){ } System.out.println("============ 数据准备完毕, 执行程序逻辑"); } }).start(); Thread.sleep(2000); new Thread(new Runnable() { @Override public void run() { prepareData(); } }).start(); } public static void prepareData(){ System.out.println("数据准备中.."); initFlag = true; System.out.println("数据准备完毕!"); } }
执行程序, 返回结果
JMM 数据原子操作
read(读取): 从主内存读取数据
load(载入): 将主内存读取到的数据写入工作内存
use(使用): 从工作内存读取数据来计算
assign(赋值): 将计算好的值重新赋值到工作内存中
strore(存储): 将工作内存数据写入主内存
write(写入): 将 store 过去的变量值赋值给主内存中的变量
lock(锁定): 将主内存变量枷锁, 表示为线程独占状态
unlock(解锁): 将主内存变量解锁, 解锁后其他线程可以锁定该变量
整个过程如下
JMM 缓存不一致问题
总线枷锁 (性能太低)
CPU 从主内存读取数据到高速缓存, 会在总线对这个数据加锁, 这样其他 CPU 没法去读或写这个数据, 直到这个 CPU 使用完整数据释放锁之后其他 CPU 才能读取该数据.
MESI 缓存一致性协议
多个 CPU 从主内存读取同一个数据到各自的高速缓存, 当其中某个 CPU 修改了缓存里的数据, 该数据会马上同步回主内存, 其他 CPU 通过总线嗅探机制可以感知到数据的变化从而将自己缓存里的数据失效.
Volatile 可见性底层实现原理
Volatile 缓存可见性实现原理
底层实现主要是通过汇编 lock 前缀指令, 它会锁定这块内存区域的缓存并回写到主内存, 此操作被称为 "缓存锁定",MESI 缓存一致性协议机制会阻止同时修改被两个以上处理器缓存的内存区域数据. 一个处理器的缓存值通过总线回写到内存会导致其他处理器响应的缓存失效.
Java 程序汇编代码查看
-server -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=compileonly,*VolatileVisibilityTest.prepareData
需要先下载 hsdis-amd64
IDEA 这样设置
显式出的结果, 其中 volatile 修饰的汇编代码如下:
0x000000000349eaff: lock add dword ptr [rsp],0h ;*putstatic initFlag ; - com.tugohost.concurrent.VolatileVisibilityTest::prepareData@9 (line 31)
可见性, 原子性与有序性
并发编程三大特性: 可见性, 原子性, 有序性
Volatile 保证可见性与有序性, 但是不保证原子性, 保证原子性需要借助 synchronized 这样的锁机制.
来源: https://www.cnblogs.com/Tu9oh0st/p/11165905.html