目录
一、jvm 内存模型
二、volatile 的作用
三、volatile 的特点
四、volatile 的实现原理
4.1 内存屏障
一、jvm 内存模型
在理解 volatile 之前,先要了解 jvm 内存模型
如上图所示,有几个关键点:
- 所有线程的共享变量都存储在主内存中,每个线程都有一个专属于自己的工作内存,每个线程都不会直接操作主内存中的变量,而是将主内存上的变量放进自己的工作内存中,只操作工作内存的数据。
- 当对工作内存修改完毕后,再将修改后的结果放回到主内存中。
- 每个线程都只能操作自己工作内存中的变量,无法直接访问其他工作内存的变量
- 线程间的变量传值只能依赖主内存完成
二、volatile 的作用
volatile 是一个修饰词,翻译为不稳定的,当一个变量被修饰后,有两个作用:
● 保证可见性
保证线程更新变量后能立刻被其他线程读到,用于保证线程读到的变量一定是最新的。
内部原理为线程修改了自己工作内存的变量后,会被立即将结果刷新会主内存中。同时其他线程读到该变量的值也作废,强制重新从主内存中读取该变量的值。
● 禁止进行指令重排序
每一行代码的执行顺序并不一定按照我们编写的顺序执行,为了提高执行效率,JVM 和 CPU 会对指令进行重新排序。而 volatile 会禁止 jvm 和 cpu 对其修饰的变量进行重排序
举个栗子
面包工坊做面包,按顺序是做好一个面包,然后将面包放进烤炉烤。而经过优化,变成先做完一批面包,再一起放入烤炉。如果将做面包的过程改为定义对象,将烤制的过程比作对象实例化,就可以节省大量的资源
三、volatile 的特点
● 无法保证原子性
即对 volatile 修饰的变量进行操作,无法保证多线程的安全,volatile 只负责保证被操作变量的原子性
● 可见性
即当多个线程访问同一个变量时,能够保证任意线程修改了变量的值,其他线程立即能够看到和使用修改的值
● 有序性
即程序执行的顺序按照代码的先后顺序执行,最有名的例子就是单例模式中的双重检查锁
public class Singleton {// singleton 作为public static volatile Singleton singleton;private Singleton() {};public static Singleton getInstance() {if (singleton == null) {synchronized (singleton) {if (singleton == null) {singleton = new Singleton();}}}return singleton;}
}
四、volatile 的实现原理
volatile 能够禁止指令排序,其实是通过内存屏障来实现的
4.1 内存屏障
内存屏障其实是一种 JVM 指令,Java 内存模型的重排规则会要求 Java 编译器在生成 JVM 指令时插入特定的内存屏障指令,通过这些内存屏障指令来禁止特定的指令重排序。
https://juejin.im/post/6844903945299558414