问题分析
/**
* @Auther: ming.wang
* @Date: 2019/1/6 19:25
* @Description:
*/
public class LazySingleton {
private static LazySingleton lazySingleton=null;
private LazySingleton() {
}
public synchronized static LazySingleton getInstance(){
if (null==lazySingleton)
{
lazySingleton=new LazySingleton();
}
return lazySingleton;
}
}
但同时也有个小小的缺憾,就是synchronized关键字开销比较大。本篇文章就来分析改如何优化上述代码。
优化
其实优化思路已经在标题中体现了,就是使用双重检查的方式来减少进锁的几率。闲言碎语不要讲,直接上代码
/**
* @Author: ming.wang
* @Date: 2019/2/20 14:45
* @Description:
*/
public class LazyDoubleCheckSingleton {
private volatile static LazyDoubleCheckSingleton instance=null;
private LazyDoubleCheckSingleton() {
}
public static LazyDoubleCheckSingleton getInstance(){
if (null==instance) {//第一个 if(instance==null),其实是为了解决代码中的效率问题,只有instance为null的时候,才进入synchronized的代码段,大大减少了几率。
synchronized (LazyDoubleCheckSingleton.class) {
if (null==instance) {//第二个if(instance==null),则是为了防止可能出现多个实例的情况。
instance=new LazyDoubleCheckSingleton();
/*
* 1.分配内存给这对象
* 2.初始化对象
* 3.设置instance指向刚刚分配的内存空间(执行完这步 instance才是非 null了)
* 其中2和3会指令重排序,执行顺序可能为123或132
* */
}
}
}
return instance;
}
}
上述代码有几处我们需要分析一下
-
i. 双重检查的含义
代码中我们使用了两处if (null==instance)空判断。第一个 if(instance==null),其实是为了解决代码中的效率问题,只有instance为null的时候,才进入synchronized的代码段,大大减少了执行synchronized的几率。第二个if(instance==null),则是为了防止可能出现多个实例的情况。
volatile阻止的不是singleton = new Singleton()这句话内部[1-2-3]的指令重排,而是保证了在一个写操作([1-2-3])完成之前,不会调用读操作(if (instance == null))。
修订
在《java并发编程的艺术》一书中,明确说明了,此处使用volatile关键字修饰的作用就是限制指令重排序(保证时序为123)。