深入理解Java内存模型JMM与volatile关键字

多核并发缓存架构

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这样的锁机制。

深入理解Java内存模型JMM与volatile关键字的更多相关文章

  1. 全面理解Java内存模型(JMM)及volatile关键字(转载)

    关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoad ...

  2. 全面理解Java内存模型(JMM)及volatile关键字(转)

    原文地址:全面理解Java内存模型(JMM)及volatile关键字 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型( ...

  3. 全面理解Java内存模型(JMM)及volatile关键字

    [版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/72772461 出自[zejian ...

  4. 深入理解 Java 内存模型 JMM 与 volatile

    Java 内存模型(Java Memory Model,简称 JMM)是一种抽象的概念,并不真实存在,它描述的是一组规范或者规则,通过这种规范定义了程序中各个变量(包括实例字段.静态字段和构成数组对象 ...

  5. 深入理解Java内存模型JMM

    本文转载自深入理解Java内存模型JMM JMM基础与happens-before 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执 ...

  6. Java并发编程:JMM (Java内存模型) 以及与volatile关键字详解

    目录 计算机系统的一致性 Java内存模型 内存模型的3个重要特征 原子性 可见性 有序性 指令重排序 volatile关键字 保证可见性和防止指令重排 不能保证原子性 计算机系统的一致性 在现代计算 ...

  7. 全面理解Java内存模型(JMM)

    理解Java内存区域与Java内存模型Java内存区域 Java虚拟机在运行程序时会把其自动管理的内存划分为以上几个区域,每个区域都有的用途以及创建销毁的时机,其中蓝色部分代表的是所有线程共享的数据区 ...

  8. Java并发指南2:深入理解Java内存模型JMM

    本文转载自互联网,侵删   一:JMM基础与happens-before 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实 ...

  9. BAT经典面试题,深入理解Java内存模型JMM

    Java 内存模型 Java 内存模型(JMM)是一种抽象的概念,并不真实存在,它描述了一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段.静态字段和构成数组对象的元素)的访问方式.试图屏 ...

随机推荐

  1. 最通俗易懂的方式让你理解 Swift 的函数式编程

    函数式编程(Functional Programming)是相对于我们常用的面向对象和面向过程编程的另外一种开发思维方式,它更加强调以函数为中心.善用函数式编程思路,可以对我们的开发工作有很大的帮助和 ...

  2. ajax默认form表单提交,导致实体不识别

    出现位置:实体比较复杂,包含List之类的时候 public class AdvertisementType { /// <summary> /// 广告位名称 /// </summ ...

  3. WPF支持OneWay,TwoWay,OneTime,Default和OneWayToSource

    原文:WPF支持OneWay,TwoWay,OneTime,Default和OneWayToSource 无论是目标属性还是源属性,只要发生了更改,TwoWay 就会更新目标属性或源属性. OneWa ...

  4. Android Camera2 拍照入门学习

    原文:Android Camera2 拍照入门学习 学习资料: 肾虚将军android camera2 详解说明 极客学院android.hardware.camera2 使用指南 Android 5 ...

  5. Windows 10开发基础——VS2015 Update1新建UWP项目,XAML设计器无法加载的解决

    这次,我们来解决一个问题...在使用Visual Studio 2015 Update 1的时候,新建一个UWP的项目,XAML设计器就会崩,具体异常信息如下图: 解决方法如下:下面圈出的那个路径就按 ...

  6. List遍历删除 或取指定的前N项

    class Program { static void Main(string[] args) { /* * List遍历删除 或取指定的前N项 */ List<PerSon> listP ...

  7. .NET MVC 在action中,过滤器中,或视图中,如何分别获取 当前请求的 控制器/视图/区域 的名字

    1)过滤器中的: public class CMSAttribute : FilterAttribute, IAuthorizationFilter { public void OnAuthoriza ...

  8. LINQ查询表达式---------into

    LINQ查询表达式---------into into 上下文关键字创建一个临时标识符,以便将 group.join 或 select 子句的结果存储到新的标识符 class Program { pu ...

  9. duilib菜单开发遇见“0xC0000005: 读取位置 0xFFFFFFFFFFFFFFFF 时发生访问冲突”

    我的程序是这样一个逻辑. 首先创建用户列表,点击列表项弹出菜单,点击菜单上“设备选项”,弹出设备列表,上面显示这个用户拥有的设备. 菜单的创建参考了这为博主的教程:http://www.cnblogs ...

  10. 通过SSIS的“查找”组件进行不同数据源之间数据的合并操作

    原文:通过SSIS的"查找"组件进行不同数据源之间数据的合并操作 为了协助开发还原生产环境中的某些bug,需要将将生产环境的某些特定表数据导入到测试环境做测试,之前一直都是暴力地t ...