单例模式的实现方法有很多种,如饿汉模式、懒汉模式、静态内部类和枚举等,当面试官问到“为什么双重效验锁要加volatile?”时,那么他指的是为什么懒汉模式中的私有变量要加 volatile?
懒汉模式指的是对象的创建是懒加载的方式,并不是在程序启动时就创建对象,而是第一次被真正使用时才创建对象。
要解释为什么要加 volatile?我们先来看懒汉模式的具体实现代码:
public class Singleton {
// 1.防止外部直接 new 对象破坏单例模式
private Singleton() {
}
// 2.通过私有变量保存单例对象【添加了 volatile 修饰】
private static volatile Singleton instance = null;
// 3.提供公共获取单例对象的方法
public static Singleton getInstance() {
if (instance == null) {
// 第 1 次效验
synchronized (Singleton.class) {
if (instance == null) {
// 第 2 次效验
instance = new Singleton();
}
}
}
return instance;
}
}
从上述代码可以看出,为了保证线程安全和高性能,代码中使用了两次 if 和 synchronized 来保证程序的执行。那既然已经有 synchronized 来保证线程安全了,为什么还要给变量加 volatile 呢?
在解释这个问题之前,我们先要搞懂一个前置知识:volatile 有什么用呢?
1.volatile 作用
volatile 有两个主要的作用,第一,解决内存可见性问题,第二,防止指令重排序。
1.1 内存可见性问题
所谓内存可见性问题,指的是多个线程同时操作一个变量,其中某个线程修改了变量的值之后,其他线程感知不到变量的修改,这就是内存可见性问题。
而使用 volatile 就可以解决内存可见性问题,比如以下代码,当没有添加 volatile 时,它的实现如下:
private static boolean flag = false;