单例模式是一种简单常用的设计模式,但是越简单的东西,其中包含的东西越不能忽略。
首先是线程不安全的单例模式:
public class UnsafeLazyInitialization { private static Instance instance; public static Instance getInstance(){ if(instance==null) //1:A线程执行 instance = new Instance(); //2:B线程执行 return instance; } //构造器私有化 private UnsafeLazyInitialization() { } }
假设A线程执行代码1的同时,B线程执行代码2,线程A可能会看到instance引用的对象还没有完成初始化,即线程不安全。
下面使用synchronized关键字使上面的代码线程安全:
public class SafeLazyInitialization{ private static Instance instance; public synchronized static Instance getInstance(){ if(instance == null) instance = new Instance(); return instance; } private SafeLazyInitialization() { } }
上面的代码使线程安全了,但是导致了另一个问题,由于getInstance()方法做了同步处理,synchronized将导致性能开销;
如果getInstance()被多个线程频繁调用,将会导致程序执行性能下降。
不过办法总是比问题多的,下面将使用双重检查锁定来解决这个问题:
public class DoubleCheckedLocking{ private static Instance instance; public static Instance getInstance(){ if(instance == null){ //1:第一次检查 synchronized (DoubleCheckedLocking.class){ //2:加锁 if (instance==null){ //3:第二次检查 instance = new Instance(); //4:问题的根源出在这里 } } } return instance; } //构造器私有化 public DoubleCheckedLocking() { } }
以上的代码是不是看起来完美无缺了呢,表面上看起来肯定使完美的,但这仍然是一个错误的优化。
问题的根源:instance = new Instance();
这一行代码可以分为如下的三行代码:
memory = allocate(); //1:分配对象的内存空间; ctorInstance(memory);//2:初始化对象; instance = memory; //3:设置instance指向刚分配的内存地址
上面3行伪代码中的2和3之间可能会被重排序,如下:
扫描二维码关注公众号,回复:
1660093 查看本文章
memory = allocate(); //1:分配对象的内存空间; instance = memory; //3:设置instance指向刚分配的内存地址,此时对象还没有初始化 ctorInstance(memory);//2:初始化对象;
即当有线程在new Instance()时,其他线程在判断if(instance==null)时,可能判断不为空,但是此时的对象还没有完成初始化,最后导致问题。
啰嗦这么半天,下面是最后的解决方案:
①基于volatile的解决方案:
/** * 线程安全的懒汉单例模式 */ public class SafeDoubleCheckedLocking{ private volatile static Instance instance; public static Instance getInstance(){ if(instance == null){ synchronized (DoubleCheckedLocking.class){ if (instance==null){ instance = new Instance(); //instance为volatile变量,现在就没问题了 } } } return instance; } private SafeDoubleCheckedLocking() { } }
②基于类初始化的解决方案:
/** * 饿汉单例模式 */ class InstanceFactory{ private static class InstanceHolder{ public static Instance instance = new Instance(); } public static Instance getInstance(){ return InstanceHolder.instance; //这里将导致InstanceHolder类被初始化 } private InstanceFactory() { } }以上就是单例模式的全部解析了,最后需要提醒的就是,单例的构造器一定要私有化!!!