Volatile关键字在多处理器开发环境中或者多线程环境下,保证共享变量的可见性。
可见性:当一个线程修改共享变量的值之后,其它线程可以立即读取到它修改的值。
如果 volatile 使用得当的话,它会比 synchronized 的成本更低,因为它不会造成线程的阻塞,也就不会导致上下文切换和调度,所以性能更好,开销更低。
Java 编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排它锁单独获得这个变量。
如果一个字段被声明为 volatile,Java 的线程内存模型(JMM)确保所有线程看到这个变量的值是一致的。
Java通过几种原子操作完成工作内存和主内存的交互:
1.lock:作用于主内存,把变量标识为线程独占状态。
2.unlock:作用于主内存,解除独占状态。
3.read:作用主内存,把一个变量的值从主内存传输到线程的工作内存。
4.load:作用于工作内存,把read操作传过来的变量值放入工作内存的变量副本中。
5.use:作用工作内存,把工作内存当中的一个变量值传给执行引擎。
6.assign:作用工作内存,把一个从执行引擎接收到的值赋值给工作内存的变量。
7.store:作用于工作内存的变量,把工作内存的一个变量的值传送到主内存中。
8.write:作用于主内存的变量,把store操作传来的变量的值放入主内存的变量中。
volatile的特殊规则就是:
所以,使用volatile变量能够保证::
由volatile关键字修饰的共享变量,在进行写操作的时候会多出一行汇编代码,该指令带有lock前缀,带有lock前缀的指令在多核处理器下会引发两个操作:
上述两个额外的事件保证了Volatile关键字的可见性。
//使用DCL(Double Check Lock)双重检查锁机制
class Singleton {
private static Singleton instance;
public int f1 = 1; // 触发部分初始化问题
public int f2 = 2;
private Singleton(){}
public static Singleton getInstance() {
if (instance == null) { // 当instance不为null时,可能指向一个“被部分初始化的对象”
synchronized (Singleton.class) {
if ( instance == null ) {
instance = new Singleton();
}
}
}
return instance;
}
}
“看起来”非常完美:既减少了阻塞,又避免了竞态条件。不错,但实际上仍然存在一个问题——当instance不为null时,仍可能指向一个"被部分初始化的对象"。
如果Singleton没有字段,自然也不会有部分初始化之说。因此,这里添加了两个字段,已触发部分初始化问题。
问题出在这行简单的赋值语句:
instance = new Singleton();
它并不是一个原子操作。事实上,它可以”抽象“为下面几条JVM指令:
memory = allocate(); //1:分配对象的内存空间
initInstance(memory); //2:初始化对象(对f1、f2初始化)
instance = memory; //3:设置instance指向刚分配的内存地址
上面操作2依赖于操作1,但是操作3并不依赖于操作2,所以JVM可以以“优化”为目的对它们进行重排序,经过重排序后如下:
memory = allocate(); //1:分配对象的内存空间
instance = memory; //3:设置instance指向刚分配的内存地址(此时对象还未初始化)
ctorInstance(memory); //2:初始化对象
可以看到指令重排之后,操作 3 排在了操作 2 之前,即引用instance指向内存memory时,这段崭新的内存还没有初始化——即,引用instance指向了一个”被部分初始化的对象”。此时,如果另一个线程调用getInstance方法,由于instance已经指向了一块内存空间,从而if条件判为false,方法返回instance引用,用户得到了没有完成初始化的“半个”单例。
解决这个该问题,只需要将instance声明为volatile变量。因为Volatile能够防止指令重排。
[1]https://monkeysayhi.github.io/2016/11/29/volatile%E5%85%B3%E9%94%AE%E5%AD%97%E7%9A%84%E4%BD%9C%E7%94%A8%E3%80%81%E5%8E%9F%E7%90%86/
[2]https://www.jianshu.com/p/64240319ed60