GOF23学习笔记(二)为什么单例模式中双重检查锁会有缺陷

双重检测锁实现

public static Singleton3 getInstance() {  
    if (instance == null) {  
        synchronized (Singleton.class) {  
            if (instance == null) {  
                instance = new Singleton();  
            }  
        }  
    }  
    return instance;  
} 

此方法将同步的内容移动到if内部,只有第一次创建才会同步,提高了效率。但是,该方法会受到指令重排序的影响

指令重排序指令重排序是为了优化指令,提高程序运行效率。指令重排序包括编译器重排序和运行时重排序。JVM规范规定,指令重排序可以在不影响单线程程序执行结果前提下进行。例如 instance = new Singleton() 可分解为如下伪代码:

memory = allocate();   //1:分配对象的内存空间  
ctorInstance(memory);  //2:初始化对象  
instance = memory;     //3:设置instance指向刚分配的内存地址  

但是经过指令重排序后会变成这样

memory = allocate();   //1:分配对象的内存空间  
instance = memory;     //3:设置instance指向刚分配的内存地址  
                       //注意,此时对象还没有被初始化!  
ctorInstance(memory);  //2:初始化对象  

这样就会出现问题,线程A执行了instance = memory(),这一步对线程B是可见,那么,线程B判断if(instance == null)时便会发现instance已经不为空了,便会返回instance,但是,由于instance只是指向了内存地址,并没有真正的初始化,那么线程B将会返回一个未能完全初始化的instance。

双重检测锁修订版

于是,修订版出现,使用两个同步块加一个局部变量来试图解决此问题,先用同步块初始化局部变量,完全初始化后赋值给instance,以此保证instance是完全初始化的。

public class Singleton3 {
	private static Singleton3 instance = null;
	
	private Singleton3() {};
	
	public static Singleton3 getInstance() {
		if(instance == null) {
			Singleton3 sc;
			synchronized (Singleton3.class) {
				sc = instance;
				if(sc == null) {
					synchronized (Singleton3.class) {
						if(sc == null) {
							sc = new Singleton3();
						}
					}
				instance = sc;
				}
			}
		}
		return instance;
	}
}
但是,这种方法依然存在问题,java规定了同步块里的内容必须在对象锁释放之前执行完毕(也就是一个线程必须执行完才能让另一个线程执行),但并没有规定同步块之外的代码,必须在同步块执行完之后执行,也就意味着, instance = sc 在运行过程中很可能跑到内层同步块中。这样,指令重排序的问题又再次出现,也就是说,sc指向内存地址后还没有初始化时就赋值给了instance,导致外部线程直接得到没有完全初始化的instance。

在JDK1.5之后,可以使用volatile变量禁止指令重排序,让DCL生效

public class Singleton3 {  
    //使用volatile修饰该对象
    private static volatile Singleton3 instance;  
  
    private Singleton3() {  
    }  
  
    public static Singleton3 getInstance() {  
        if (instance == null) {  
            synchronized (Singleton3.class) {  
                if (instance == null) {  
                    instance = new Singleton3();  
                }  
            }  
        }  
        return instance;  
    }  
}
 

猜你喜欢

转载自blog.csdn.net/icydate/article/details/80307969
今日推荐