3 DCL懒汉式 (双重检测)
DCL是在懒汉式的基础上修改的
懒汉式
public class SingletonDemo2 {
//优缺点
// 优 线程安全 可以延迟加载
// 缺点 效率不高
// 私有化构造器
private SingletonDemo2() {}
// 创建但不初始化
private static SingletonDemo2 INSTANCE;
public static synchronized SingletonDemo2 getInstance() {
if (INSTANCE == null) {
INSTANCE = new SingletonDemo2();
}
return INSTANCE;
}
}
有synchronized可以保证线程安全 但是只有每次同步请求 会造成性能上的浪费
那我们吧 synchronized 放到里面 并顺带加一个判断
public class SingletonDemo3 {
// 1. 私有化构造器
private SingletonDemo3() {}
// 2.初始化单例
private static SingletonDemo3 instance ;
// 3.全局访问方法
public static SingletonDemo3 getInstance() {
if (instance == null) {
synchronized (SingletonDemo3.class) {
if (instance == null) {
instance = new SingletonDemo3();
}
}
}
return instance;
}
}
现在假设有两个线程 线程一 线程二
线程一抢先进入getInstance()方法 并获取到了类锁 开始执行 synchronized 的代码块儿 这时候instance 是空的 要new给线程一
这时候 创建实例 会有几个过程
- 分配内存
- 初始化构造器
- 指向内存地址 这时候instance才不是空
由于指令重排 可能这三个步骤就会不是这样 可能变成这样
- 分内存
- 指向内存地址
- 初始化构造器
可看到 没有执行到 初始化构造器 的时候instance已经不为空了 但是又没有初始化 如果 这时候线程二进到了getInstance() 那么就会直接判定instance不为空 那就直接被线程二拿走用了 但是这时候又没有初始化 所以
就会报错
给instance加一个volatile关键字
public class SingletonDemo3 {
// 1. 私有化构造器
private SingletonDemo3() {}
// 2.初始化单例
private static volatile SingletonDemo3 instance ;
// 3.全局访问方法
public static SingletonDemo3 getInstance() {
if (instance == null) {
synchronized (SingletonDemo3.class) {
if (instance == null) {
instance = new SingletonDemo3();
}
}
}
return instance;
}
}
就可以了