双重检验锁方式实现单例模式的原理是什么?

单例模式大概是Java编程中最常用的设计模式之一了,之前也有文章说过什么是单例模式,链接如下:

https://blog.csdn.net/weixin_39309402/article/details/98126883

虽然这篇文章中也分析了如何利用同步锁机制保证懒汉式单例模式的线程安全问题,同步方法,同步代码块等,但都非最优的解决方法,今天我们就来讲讲什么是双重检验锁方式实现单例模式,包括它的特点和原理。

/**
 * 双重检验锁方式实现单例模式
 */
public class DualLazySingleTon {
	// 静态实例变量
	private volatile static DualLazySingleTon instance;

	// 私有化构造函数
	private DualLazySingleTon() {
		System.out.println(Thread.currentThread().getName() + "\t" + "进入构造方法");
	}

	// 静态public方法,向整个应用提供单例获取方式
	public static DualLazySingleTon getInstance() {
		if (instance == null) { //第1重判断
			synchronized (DualLazySingleTon.class) {
				if (instance == null) { //第2重判断
					instance = new DualLazySingleTon();	
				}
			}
		}
		return instance;
	}

}
/**
 * 查看双重检验锁方式实现单例模式在多线程环境下线程是否安全
 */
public class DualLazyMyThread extends Thread {

	@Override
	public void run() {
		System.out.println(DualLazySingleTon.getInstance().hashCode());
	}

	public static void main(String[] args) {

		DualLazyMyThread[] mts = new DualLazyMyThread[10];
		for (int i = 0; i < mts.length; i++) {
			mts[i] = new DualLazyMyThread();
		}

		for (int j = 0; j < mts.length; j++) {
			mts[j].start();
		}
	}
}

第一次判断是否为null:
第一次判断是在Synchronized同步代码块外,理由是单例模式只会创建一个实例,并通过getInstance方法返回singleton对象,所以如果已经创建了singleton对象,就不用进入同步代码块,不用竞争锁,直接返回前面创建的实例即可,这样大大提升效率。

第二次判断singleton是否为null:
第二次判断原因是为了保证同步,假若线程A通过了第一次判断,进入了同步代码块,但是还未执行,线程B就进来了(线程B获得CPU时间片),线程B也通过了第一次判断(线程A并未创建实例,所以B通过了第一次判断),准备进入同步代码块,假若这个时候不判断,就会存在这种情况:线程B创建了实例,此时恰好A也获得执行时间片,如果不加以判断,那么线程A也会创建一个实例,就会造成多实例的情况。

所以,为了满足单例模式的要求,双重校验是必不可少的。

声明变量时为什么要用volatile关键字进行修饰?

volatile关键字可以防止jvm指令重排优化,使用了volatile关键字可用来保证其线程间的可见性和有序性;

因为对象的创建并非一步完成,而是需要分为3个步骤执行的,比如:singleton = new Singleton();  

指令1:获取singleton对象的内存地址
指令2:初始化singleton对象
指令3:将内存地址指向引用变量singleton

因为Volatile禁止JVM对指令进行重排序,所以创建对象时会严格按照指令1-2-3的顺序执行,假若如果没有Volatile关键字,单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。比如
线程A正常创建一个实例,执行了1-3,此时线程B调用getInstance()后发现instance不为空,因此会直接返回instance,但此时instance并未被初始化,所以需要用volatile关键字修饰。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章