DCL(双重检测锁) 机制不一定线程安全,原因是有指令重排的存在,加入volatile可以禁止指令重排。
原因在于创建一个对象并不是原子操作,单线程的指令重排没有问题,但是多线程时,某一个线程在执行到第一次检测,读取到的instance不为null时,instance的引用对象可能没有完成初始化。
public class SingletonTest {
// 禁止指令重排
private static volatile SingletonTest instance = null;
private SingletonTest() {
System.out.println(Thread.currentThread().getName() + "\t 构造方法");
}
/**
* 双重检测机制
*
* @return
*/
public static SingletonTest getInstance() {
if (instance == null) {// 这可能出问题!!!
synchronized (SingletonTest.class) {
if (instance == null) {
//下面一行代码的字节码: 21和24可能指令重排,其他线程在synchronized外面可能return还未创建完成的实例
//17: new #3 创建对象,将对象引用入栈
//20: dup 复制一份对象引用
//21: invokespecial #4 利用一个对象引用,调用构造方法
//24: putstatic #2 利用一个对象引用,赋值给 instance
instance = new SingletonTest();
}
}
}
return instance;
}
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
SingletonTest.getInstance();
}, String.valueOf(i)).start();
}
}
}