双重校验锁实现对象单例
package com.heu.wsq.basic;
/**
* 双重校验所锁实现对象单例(线程安全)
* @author wsq
* @date 2021/1/24
*/
public class Singleton {
/**
* volatile作用:
* 1.保证内存可见性
* 2.防止指令重排序
*/
private volatile static Singleton uniqueInstance;
private Singleton(){}
public static Singleton getInstance(){
// 判断对象是否已经实例过,没有实例化过才进入加锁代码
if (uniqueInstance == null){
// 由于singleton属于这个类,因此给Singleton加锁
synchronized(Singleton.class){
if (uniqueInstance == null){
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
为什么要使用volatile修饰uniqueInstance?
uniqueInstance 采⽤ volatile 关键字修饰也是很有必要的, uniqueInstance = new Singleton(); 这 段代码其实是分为三步执⾏:
- 为 uniqueInstance 分配内存空间
- 初始化 uniqueInstance
- 将 uniqueInstance 指向分配的内存地址
但是由于 JVM 具有指令重排的特性,执⾏顺序有可能变成 1->3->2。指令重排在单线程环境下不 会出现问题,但是在多线程环境下会导致⼀个线程获得还没有初始化的实例。例如,线程 T1 执 ⾏了 1 和 3,此时 T2 调⽤ getUniqueInstance () 后发现 uniqueInstance 不为空,因此返回 uniqueInstance ,但此时 uniqueInstance 还未被初始化。