public class DoubleCheckedLocking {
private static DoubleCheckedLocking instance;
public static DoubleCheckedLocking getInstance(){
if(instance == null){
// 4.第一次检查
synchronized (DoubleCheckedLocking.class){
// 5.加锁
if(instance == null){
// 6. 第二次检查
instance = new DoubleCheckedLocking(); // 7.问题的根源处在这里
}
}
}
return instance;
}
}
上面代码有一个问题:
在线程执行到第4行,代码读取到 instance 不为 null 时,instance引用的对象有可能还没有完全初始化。
问题的根源:
前面的 双重检查锁定上述代码的第7行(instance = new DoubleCheckedLocking())创建了一个对象。这一行代码可以分解为 下面的3行代码。
memory = alloct(); //:1:分配对象的内存空间
ctorinstance(memory); //2: 初始化对象
instace = memory; //3:设置instace指向刚分配的内存地址
上面的3行伪代码中的2和3之间,可能被重排序,重排序如下:
memory = alloct(); //:1:分配对象的内存空间
instace = memory; //3:设置instace指向刚分配的内存地址
// 主要此对象还没有被初始化
ctorinstance(memory); //2: 初始化对象
多线程执行结果如下:
时间 | 线程A | 线程B |
---|---|---|
t1 | A1: 分配对象的内存空间 | |
t2 | A3: 设置instance 执行内存空间 | |
t3 | B1: 判断instance 是否为空 | |
t4 | B2: 由于instance不为null, 线程B 将访问instance 引用的对象 | |
t5 | A2: 初始化对象 | |
t6 | A4: 访问instance 引用的对象 |
解决方案: 加上volatile关键字
public class DoubleCheckedLocking {
private static volatile DoubleCheckedLocking instance;
public static DoubleCheckedLocking getInstance(){
if(instance == null){
// 4.第一次检查
synchronized (DoubleCheckedLocking.class){
// 5.加锁
if(instance == null){
// 6. 第二次检查
instance = new DoubleCheckedLocking(); // 7.问题的根源处在这里
}
}
}
return instance;
}
史上最全的并发编程脑图:https://www.processon.com/view/5b1f1ad7e4b03f9d251c06e5#map