volatile关键字

前言:volatile可以说是JVM提供的最轻量级的同步机制,人们常说volatile保证变量的可见性和禁止重排序,那么怎么理解呢?应该从Java内存模型说起。

一、可见性

  由于处理器和内存的运算速度存在较大差异,为了保证效率,现代计算机引入读写速度接近内存读取速度的高速缓存区,这样一来就有了内存一致性问题,为了解决这个问题,计算机又在高速缓存和主内存之间引入一层一致性协议。

  Java内存模型没有直接使用计算机指定的寄存器和高速缓存,但是采用了同样的思路。如下图

 

  Java规定所有变量都存储到主内存当中,这里不包括局部变量和方法参数(进程特有),所有变量的传递操作必须在线程、工作内存和主内存三者之间进行。

主内存和工作内存之间的协议,除此之外Java规定了8个原子操作,lock、unLock、read、load、use、assgin、stroe、write,这里把后六个操作两两合并,简单理解为Read(从主内存中读取传递给工作内存、赋值给工作内存)Use(传递给执行线程、执行后赋值给工作内存)Write(从工作内存中读取传递给主内存、写入主内存)

  但是多线程并发时,每个原子性操作之间可能有其他线程的操作插入,这时候使用volatile修饰变量可以保证两点:Use前Read,Use后立即Write。这样保证变量每次使用前用的都是最新的值,使用后立刻同步到主内存中,但是Use操作并不是原子性的,线程运算前变量是最新值,并不代表运算时为最新值,所以volatile只保证了工作内存和主内存读写时的一致性,即变量可见性

二、禁止重排序

  int i = 1;
  int j = 2;

  JVM进行编译时,会有一些指令重排的优化,如上面两行代码,它们的执行顺序不会改变结果,所以JVM在编译成字节码文件时,不一定保证这两行代码的执行顺序。在汇编层面,volatile的实现方式是在两行指令中间加了一个lock add0的空操作,相当于加了一层内存屏障,完成前面说的Store和Write操作,立即写入主内存。

 

  volatile的应用在于在单例的经典实现DCL(Double Check Lock)双重检查锁中,使用volatile禁止重排序。

public class Singleton {
private static volatile Singleton instance;

private Singleton() {
}

public static Singleton getInstance() {
if (instance != null) {
synchronized (Singleton.class) {
if (instance != null) {
instance = new Singleton();
}
}
}
return instance;
}
}

public class SingletonTest {
    public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
}
}

 

  下面Object o = new Object();的字节码指令 

  NEW java/lang/Object
  DUP
  INVOKESPECIAL java/lang/Object.<init> ()V
  ASTORE 2

  那么一个对象的创建过程大致分为三步:分配内存地址,初始化变量,将内存地址赋值给变量,但是第二步和第三步之的执行排序并不影响执行结果,所以有可能发生重排序

  所以当instance = new Singleton();发生指令重排时,如果此时一个线程执行完上述的第三步,还没执行第二步,那么其他线程进来通过 getInstance方法,就有可能获得一个变量没有初始化的对象,导致运行结果错误。volatile禁止重排序就解决了上述问题。

总结:volatile保证了变量在工作内存使用前和使用后和主内存中的一致性问题,但没有保证使用过程中的原子性,所以还是有线程不安全的问题,这就是volatile作为轻量级同步机制的可见性。为了实现可见性,volatile修饰的变量在编译成二进制时,会在运算赋值后,加入一层内存屏障(lock add0),这个指令同时保证了指令不会重排序。

posted @   小皮睡不醒  阅读(55)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示