之前写过单例模式,但是没有对双重校验锁进行详细分析,今天就来分析一下
@NoArgsConstructor
public class Singleton {
// volatile 作用:
// 1、保证变量可见性;
// 2、禁止指令重排序;
// instance 会经过三个步骤: 1、为instance分配内存空间 2、调用构造函数初始化instance 3、为instance分配内存地址
// 假设没有使用volatile,因为这个cpu会对指令进行排序,那么顺序可能会变成 1、3、2
// 那么在进行判断的时候,会导致instance==null 结果是false,实际上instance还没有被实例化,这时候使用的时候回报空指针异常。
private static volatile Singleton instance;
public static Singleton getInstance() {
// 此处的instance判断是为了节省线程时间,防止每个线程都走加锁逻辑。假设线程A和线程B同时进来,那么会走后面的if判断。
// 之后线程A实例化instance之后,线程B也获取到锁,发现已经创建了instance,会直接返回,线程C执行的时候在第一个if判断发现instance已经实例化了,就不会走加锁逻辑了。
if (instance == null) {
synchronized (Singleton.class) { // 假设有三个线程A,B,C,三个线程同时到达这里,由于加了同步锁,因此只会有一个线程获取到锁。那么假设线程A获取到锁,线程B,C在此等待
if (instance == null) { // 这一步判断instance为空,进行实例化。那么,假设没有这个if判断,在线程A执行完成之后,线程B,C进来之后会对instance进行再度实例化。
instance = new Singleton();
}
}
}
return instance;
}
}