单例模式1.0:
public class Singleton {private static Singleton sInstance;public static Singleton getInstance() {if (sInstance == null) { // 1sInstance = new Singleton();}return sInstance;}private Singleton() {}}
这种方式很辣鸡,因为多线程环境下不能保证单例。
单例模式2.0:
public class Singleton {private static volatile Singleton sInstance;public static synchronized Singleton getInstance() {if (sInstance == null) {sInstance = new Singleton();}return sInstance;}private Singleton() {}}
这种方式也很辣鸡,因为多线程环境下每个线程执行getInstance()都要阻塞,效率很低。
单例模式3.0:
public class Singleton {private static Singleton sInstance;public static Singleton getInstance() {if (sInstance == null) { // 位置1synchronized (Singleton.class) {if (sInstance == null) {sInstance = new Singleton(); // 位置2}}}return sInstance;}private Singleton() {}}
这种方式使用双重检查锁,多线程环境下执行getInstance()时先判断单例对象是否已经初始化,如果已经初始化,就直接返回单例对象,如果未初始化,就在同步代码块中先进行初始化,然后返回,效率很高。
但是这种方式是一个错误的优化,问题的根源出在位置2
sInstance =new Singleton();这句话创建了一个对象,他可以分解成为如下3行代码:
memory = allocate(); // 1.分配对象的内存空间ctorInstance(memory); // 2.初始化对象sInstance = memory; // 3.设置sInstance指向刚分配的内存地址
上述伪代码中的2和3之间可能会发生重排序,重排序后的执行顺序如下
memory = allocate(); // 1.分配对象的内存空间sInstance = memory; // 2.设置sInstance指向刚分配的内存地址,此时对象还没有被初始化ctorInstance(memory); // 3.初始化对象
因为这种重排序并不影响Java规范中的规范:intra-thread sematics允许那些在单线程内不会改变单线程程序执行结果的重排序。
但是多线程并发时可能会出现以下情况

线程B访问到的是一个还未初始化的对象。
解决方案1:
public class Singleton {private static volatile Singleton sInstance;public static Singleton getInstance() {if (sInstance == null) { // 位置1synchronized (Singleton.class) {if (sInstance == null) {sInstance = new Singleton(); // 位置2}}}return sInstance;}private Singleton() {}}
将对象声明为volatitle后,前面的重排序在多线程环境中将会被禁止
解决方案2:
public class Singleton {private Singleton(){};private static class Inner{private static Singleton SINGLETION=new Singleton();}public static Singleton getInstance(){return Inner.SINGLETION;}}
静态内部类不会随着外部类的初始化而初始化,他是要单独去加载和初始化的,当第一次执行getInstance方法时,Inner类会被初始化。
静态对象SINGLETION的初始化在Inner类初始化阶段进行,类初始化阶段即虚拟机执行类构造器()方法的过程。
虚拟机会保证一个类的()方法在多线程环境下被正确的加锁和同步,如果多个线程同时初始化一个类,只会有一个线程执行这个类的()方法,其它线程都会阻塞等待。
