volatile 关键字的底层实现原理

参考书籍:《Java 并发编程的艺术》 - 方腾飞 魏鹏 程晓明著

在并发编程中,synchronized 和 volatile 这两个关键字都扮演着重要的角色,volatile 是轻量级的 synchronized,它在多处理器开发中保证了共享变量的 “可见性”。

那么什么是 “可见性” 呢?

可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值

如果 volatile 使用得当的话,它会比 synchronized 的成本更低,因为它不会造成线程的阻塞,也就不会导致上下文切换和调度,所以性能更好,开销更低。
 

volatile 定义

Java 语言规范第 3 版对 volatile 的定义如下:

Java 编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排它锁单独获得这个变量。

Java 提供了 volatile,在某些情况下比锁要更加方便。

如果一个字段被声明为 volatile,Java 的线程内存模型(JMM)确保所有线程看到这个变量的值是一致的。

volatile 实现原理

看一个简单的例子:

private volatile Singleton instance = null;

public Singleton getInstance(){
        if(instance == null){
            instance = new Singleton(); 
        }
        return instance;
}

instance 这个成员变量是被 volatile 关键字修饰的,所以 instance 这个引用的改变对于各个线程来说是 “可见的”。那么 volatile 是如何保证可见性的呢?

我们在 x86 处理器下通过工具获取 JIT 编译器生成汇编指令来查看对 volatile 进行写操作时,CPU 会做什么事情(就是这句代码:instance = new Singleton();)。

转成汇编代码如下:

0x01a3deld: movb $0x0,0x1104800(%esi);0x01a3de24: lock add1 $0x0,(%esp);

有 volatile 变量修饰的共享变量进行写操作的时候会多出第二行汇编代码,Lock 前缀的指令在多核处理器下会引发两件事情:

  • 将当前处理器缓存行的数据写回到系统内存。
  • 这个写回内存的会使在其他 CPU 里缓存了该内存地址的数据无效。

正是这两个额外的事件,使得 volatile 保证了共享变量的 “可见性”。

猜你喜欢

转载自blog.csdn.net/u013568373/article/details/94477266