深入理解Java内存模型JMM与volatile关键字
深入理解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(){

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通过
总线嗅探机制可以感知到数据的变化从而将自己缓存里的数据失效。
- 多个CPU从主内存读取同一个数据到各自的高速缓存,当其中某个CPU修改了缓存里的数据,该数据会马上同步回主内存,其他CPU通过

Volatile可见性底层实现原理
Volatile缓存可见性实现原理
- 底层实现主要是通过汇编lock前缀指令,它会锁定这块内存区域的缓存并回写到主内存,此操作被称为“缓存锁定”,MESI缓存一致性协议机制会阻止同时修改被两个以上处理器缓存的内存区域数据。一个处理器的缓存值通过总线回写到内存会导致其他处理器响应的缓存失效。
Java程序汇编代码查看
- -server -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=compileonly,*VolatileVisibilityTest.prepareData
- 需要先下载hsdis-amd64

IDEA这样设置
显式出的结果,其中volatile修饰的汇编代码如下:
0x000000000349eaff:
lockadd dword ptr [rsp],0h ;*putstatic initFlag
; - com.tugohost.concurrent.VolatileVisibilityTest::prepareData@9 (line 31)
可见性、原子性与有序性
- 并发编程三大特性:可见性,原子性,有序性
- Volatile保证可见性与有序性,但是不保证原子性,保证原子性需要借助synchronized这样的锁机制。
深入理解Java内存模型JMM与volatile关键字的更多相关文章
- 全面理解Java内存模型(JMM)及volatile关键字(转载)
关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoad ...
- 全面理解Java内存模型(JMM)及volatile关键字(转)
原文地址:全面理解Java内存模型(JMM)及volatile关键字 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型( ...
- 全面理解Java内存模型(JMM)及volatile关键字
[版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/72772461 出自[zejian ...
- 深入理解 Java 内存模型 JMM 与 volatile
Java 内存模型(Java Memory Model,简称 JMM)是一种抽象的概念,并不真实存在,它描述的是一组规范或者规则,通过这种规范定义了程序中各个变量(包括实例字段.静态字段和构成数组对象 ...
- 深入理解Java内存模型JMM
本文转载自深入理解Java内存模型JMM JMM基础与happens-before 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执 ...
- Java并发编程:JMM (Java内存模型) 以及与volatile关键字详解
目录 计算机系统的一致性 Java内存模型 内存模型的3个重要特征 原子性 可见性 有序性 指令重排序 volatile关键字 保证可见性和防止指令重排 不能保证原子性 计算机系统的一致性 在现代计算 ...
- 全面理解Java内存模型(JMM)
理解Java内存区域与Java内存模型Java内存区域 Java虚拟机在运行程序时会把其自动管理的内存划分为以上几个区域,每个区域都有的用途以及创建销毁的时机,其中蓝色部分代表的是所有线程共享的数据区 ...
- Java并发指南2:深入理解Java内存模型JMM
本文转载自互联网,侵删 一:JMM基础与happens-before 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实 ...
- BAT经典面试题,深入理解Java内存模型JMM
Java 内存模型 Java 内存模型(JMM)是一种抽象的概念,并不真实存在,它描述了一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段.静态字段和构成数组对象的元素)的访问方式.试图屏 ...
随机推荐
- 二维码彩色广告招牌的切割制作问题(C#.net下对彩色二维码圆角样式及改进)
原文:二维码彩色广告招牌的切割制作问题(C#.net下对彩色二维码圆角样式及改进) 我们知道,目前二维码还很少用于广告招牌的制作.但随着智能手机越来越普及,互联网等网络的应用也越来越广泛,作为连接物理 ...
- WPF 绑定到集合
<Window x:Class="CollectionBinding.MainWindow" xmlns="http://schemas.micros ...
- .Net 通过Cmd执行Adb命令 /c参数
通过cmd.exe来执行adb命令,可以进行一些命令组合,直接用adb.exe的话只能执行单个adb命令 这里要注意cmd 中的/c参数,指明此参数时,他将执行整个字符串中包含的命令并退出当前cmd运 ...
- Reverse Engineering Custom DataTypes -> GUID() in SQL Server to PostgreSQL
原文 https://archive.sap.com/discussions/thread/3641585 First you reverse engineer from a script where ...
- docker启动命令,docker重启命令,docker关闭命令
启动 systemctl start docker 守护进程重启 sudo systemctl daemon-reload 重启docker服务 systemctl restar ...
- Python:Pandas学习
import pandas as pd import numpy as np s = pd.Series([1, 3, 6, np.nan, 44, 1]) df= pd.DataFrame(np.r ...
- DELPHI之关于String的内存分配(引)
在函数.过程或者方法中定义一个字符串变量时,由于我们知道在函数.过程或者方法中定义的变量为局部变量,它的内存 是在栈中分配的,但是这里有个小细节我们要注意,对于一个局部的字符串变量,它的大小为4字节, ...
- Web 高效开发必备的 PHP 框架
摘要: 每周为您推送最有价值的开源技术内参! 码云项目推荐 1.项目名称:多功能 THinkPHP 开源框架 项目简介:使用 THinkPHP 开发项目的过程中把一些常用的功能或者第三方 sdk 整合 ...
- SYN2306A型 GPS北斗双模授时板
SYN2306A型 GPS北斗双模授时板 北斗gps时钟北斗授时设备北斗时钟同步系统使用说明视频链接: http://www.syn029.com/h-pd-211-0_310_36_-1.html ...
- shell多线程(2)之基于管道实现并发
在shell脚本里批量执行程序是比较常见的方式,如果程序很多,每个执行时间比较长,则顺序执行需要花费大量的时间. 此时并发就成为我们考虑的方向. 上篇<shell多线程>中我们已经简单实现 ...