深入汇编语言理解volatile关键字

简介: 深入汇编语言理解volatile关键字

接着上一篇写,一致性的协议还有加锁的过程,要搞懂volatile关键字原理的话,需要研究它的源代码,底层是用 c语言来实现的。

一、上一篇的代码如下:

  1. public class VolatileTest {
  2.    private  static volatile boolean  initFlag=false;

  3.    public static void main(String[] args) throws InterruptedException {
  4.        new Thread(new Runnable() {
  5.            public void run() {
  6.                System.out.println("waiting  data.....");
  7.                while(!initFlag){
  8.                }
  9.                System.out.println("-------------------------success");
  10.            }

  11.        }).start();
  12.        Thread.sleep(2000);
  13.        new Thread(new Runnable() {
  14.            public void run() {
  15.                prepareData1();
  16.            }
  17.        }).start();
  18.    }
  19.    public  static  void  prepareData1(){
  20.        System.out.println("prepareing data.....");
  21.        initFlag=true;
  22.        System.out.println("prepare data end.....");
  23.    }

  24. }

二、查看下volatile关键字在汇编语言如何去实现:

汇编语言的指令码,看下上面的代码的汇编指令的样子

1、借助下工具:需要把下面的两个文件放入到java/jdk/jre/bin的目录下

dll文件是底层用c语言实现的,还有一个lib的包

2、在运行上面的程序的过程中:会把汇编语言给打印出来

  1. -server -Xcomp-XX:+UnlockDiagnosticVMOptions-XX:+PrintAssembly-XX:CompileCommand=compileonly,*VolatileVisibility.prepareDate

3、在运行的时候在VM options下添加上面的参数,然后选择jre的目录。

4、运行的结果如下:下面的这一行代码时机对应的代码是在24行的initFlag=true;  赋值。

而且这个变量是用volatile来修饰的

如果不加volatile关键字的话,就不会在汇编语言这条指令下有lock指令。

5、解释下这条指令是做什么的:  

在上面的add dword  ptr [rsp],0h这条指令就是对工作内存赋值操作。rsp:是高速缓存的寄存器。赋值的操作是对应下面的assign的操作。把这个值赋给到工作缓存里面。

所以说assign的底层大概做的add dword  ptr [rsp],0h就是这个操作

注意:这个lock指令非常的关键,就可以保证volatile关键字缓存可见性的问题。

三、Volatile缓存可见性实现原理

底层实现主要是通过汇编Lock前缀指令,它会锁定 这块内存区域的缓存(缓存行锁定)

并回写到主内存

IA-32架构软件开发者手册对lock指令的解释,下面的是Lock指令帮我们做两个事情。

1):会将当前处理器缓存行的数据立即写回到系统内存。

一旦加了volatile关键字的话一旦工作内存的值发生改变的话会马上同步到主内存,经过到总线的时候,其他的线程把自己的工作内存的值就马上失效掉。然后其他线程就会马上拿到主内存中最新的数据。

如果没有volatile关键字的话,它不一定会把工作内存的数据同步到主内存,因为改了工作内存里面的值,后面还有很多的代码在执行,

解决昨天的问题:有可能其他的线程拿到的值还是老的数据,然后请看下面第二的特征:

2):这个写回内存的操作会引在其他CPU里缓存了该内存地址的数据无效(MESI协议)

在这里会激发MESI缓存一致性协议,和开启嗅探机制,如果不加volatile关键字的话不会触发其他线程的嗅探机制和MESI缓存一致性协议,而且其他工作内存的值不会实现

所以说必须有volatile关键字,说白了,就必须有lock指令。所以说面试的时候当问到这个问题的时候不能说volatile关键字保证变量的可见性。可以结合上面的内存模型给面试官讲明白。

3):加上volatile关键字之后它还有一个特性-加锁

volatile的加锁只是对缓存行加锁,而加锁的时机是这样的:

在回写主内存之前,也就是在执行store操作之前它会对缓存行做一个操作:lock操作,直到write执行完,做一个unlock操作,这样的操作就解决了,线程1还没有执行完回显到主内存,其他线程就来读取旧值的问题。

解决:多个线程线程同时执行的问题,可以让先抢到lock锁的去回显主内存的值。

上面的过程就是volatile底层的实现原理

volatile的加锁的实现和总线加锁的区别:

1、下面的图是总线加锁的过程:总线加锁的粒度是很大的:read->load->use->assign->store->write:会经历这么多的步骤,而且如果计算的量很大的话。其他的线程需要等待很长的时间去执行。

2、而在volatile加锁的过程的粒度是很小的:store->wirte:这个步骤就是对主内存中的变量赋值而已,主内存对一个变量的赋值,每秒会支撑几十万,甚至上百万的变量来赋值,变量的赋值时间可能是0.几几毫秒的时间,可以忽略不计。所以volatile关键字能够让多线程同时的去并行执行,又解决了可见性的问题,而且效率是非常之高。


明天继续写深入了解并发编程的可见性,原子性,有序性。,谢谢大家的关注与支持!!

调试一个初次见到的代码比重写代码要困难两倍。因此,按照定义,如果你写代码非常巧妙,那么没有人足够聪明来调试它。— Brian W. Kernighan

相关文章
|
4月前
|
存储 缓存 安全
learn_C_deep_9 (汇编角度理解return的含义、const 的各种应用场景、volatile 的基本理解与实验证明)
learn_C_deep_9 (汇编角度理解return的含义、const 的各种应用场景、volatile 的基本理解与实验证明)
|
存储 SQL 缓存
深入汇编指令理解Java关键字volatile
深入汇编指令理解Java关键字volatile
120 0
|
4月前
|
存储 Unix 编译器
汇编语言----X86汇编指令
汇编语言----X86汇编指令
98 1
|
4月前
|
存储 机器学习/深度学习 移动开发
汇编语言指令系列
汇编语言指令系列
233 0
|
2月前
|
存储 机器学习/深度学习 芯片
8086 汇编笔记(十二):int 指令 & 端口 & 直接定址表
8086 汇编笔记(十二):int 指令 & 端口 & 直接定址表
|
2月前
|
存储 算法 安全
深入理解汇编语言:基础语法和常用指令介绍
深入理解汇编语言:基础语法和常用指令介绍
|
3月前
汇编语言(第四版) 实验一 查看CPU和内存,用机器指令和汇编指令编程
汇编语言(第四版) 实验一 查看CPU和内存,用机器指令和汇编指令编程
8086 汇编笔记(九):call 指令 和 ret 指令
8086 汇编笔记(九):call 指令 和 ret 指令