JVM 之 volatile可见性、禁止指令重排序的JVM实现

volatile (可见性、禁止指令重排序)

介绍思路:volatile是什么?解决了什么问题?怎么实现的?有什么可以扩展的?

volatile是什么?

volatile是java提供的轻量级的锁(在jdk6锁优化前可以说是最轻量级)

volatile解决了什么问题?

  • 可见性:保证变量修改后对所有线程可见
  • 禁止指令重排序:禁止指令重排序对程序并发执行所产生的干扰

volatile怎么实现的?

  • volatile的可见性,是通过 各个线程 对volatile所声明的变量 进行操作前,并不是直接使用工作内存中的主内存副本,而是于主内存中加载最新的数据后再使用。
    volatile修饰的变量值更新后并不是立即对所有线程可见,而是类似懒加载的形式,使用时可见。
  • volatile的禁止指令重排序,是通过设置内存屏障实现的,我理解就是通过内存屏障将代码块中的指令分割为了两部分,以保证内存屏障前的指令全都先于内存屏障后的指令。

内存屏障怎么实现的?干了点啥?

内存屏障实际是通过插入一条lock指令实现的,lock指令会将当前线程的变量值写回主内存,根据可见性,也就是说所有线程中在使用该变量副本前都会同步到此次写入的值。

内存屏障详解(可看可不看)

内存屏障实际是执行了一个"lock"操作(完整指令为:"addl$0x0,(%esp)"(把ESP寄存器的值加0))。

lock的作用是将本处理器的缓存写入了内存,该写入动作也会引起别的处理器或者别的内核无效化(Invalidate)其缓存,

所以这种操作相当于对缓存中的变量做了一次前面介绍Java内存模式中所说的"store和write"操作 [4] 。

所以通过这样一个空操作,可让前面volatile变量的修改对其他处理器立即可见。

volatile实际使用时需注意的点:

volatile的读写性能

  • volatile修饰的变量在读取上与普通变量的读取性能几乎相同,但写入则会慢上一些,因为需要在指令中插入内存屏障。

volatile可见性不适用的场景

  • 运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值。
    例如:volatile i ; i++; i++是出栈 +1 入栈的操作,也就是说在出栈时i是最新值,但+1操作进行时,i的值可能已被其他线程改变,最终导致入栈操作时的i不正确。
  • 变量不需要与其他的状态变量共同参与不变约束。
    例如:volatile boolean flag = true; while(flag){}; 其他线程执行flag=false时,while立即停止。

扩展:对volatile关键字的理解离不开 -> java的内存模型

java的内存模型:

  • java中的所有变量都存储在主内存中
  • java线程会维护一个自身工作内存,其中保存了该线程自己使用到的变量的主内存副本,该线程的所有操作都在工作内存进行(保证了线程间的隔离)

线程间的数据传递怎么进行?

  • 线程间的数据传递通过主内存进行,即线程、工作内存、主内存间的交互进行传递。
    也就是,需要A线程先将变量X的新值刷回主线程,B线程再于主线程内获取到变量X的新值,如此完成线程间的数据传递。
相关推荐
1.01^10006 小时前
[5-01-01].第04节:初识字节码文件 - 字节码文件作用
jvm
找不到、了10 小时前
JVM核心知识整理《1》
jvm
L.EscaRC11 小时前
面向 Spring Boot 的 JVM 深度解析
jvm·spring boot·后端
学到头秃的suhian1 天前
JVM-类加载机制
java·jvm
NEFU AB-IN1 天前
Prompt Gen Desktop 管理和迭代你的 Prompt!
java·jvm·prompt
唐古乌梁海2 天前
【Java】JVM 内存区域划分
java·开发语言·jvm
众俗2 天前
JVM整理
jvm
echoyu.2 天前
java源代码、字节码、jvm、jit、aot的关系
java·开发语言·jvm·八股
代码栈上的思考2 天前
JVM中内存管理的策略
java·jvm
thginWalker2 天前
深入浅出 Java 虚拟机之进阶部分
jvm