双重检查锁定(Double Check Lock,DCL)html
一、懒汉式单例模式,没法保证线程安全:安全
public class Singleton { private static Singleton singleton; private Singleton() { } public static Singleton getInstance() { if (singleton == null) {// 多个线程同时执行到此,会生成多个Singleton实例 singleton = new Singleton(); } return singleton; } }
二、同步处理,synchronized就会致使这个方法比较低效:多线程
public class Singleton { private static Singleton singleton; private Singleton() {} public static synchronized Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } }
三、双重检查 DCL:并发
public class Singleton { private static Singleton singleton; Integer a; private Singleton(){} public static Singleton getInstance(){ if(singleton == null){ // 1 只有singleton==null时才加锁,性能好 synchronized (Singleton.class){ // 2 if(singleton == null){ // 3 singleton = new Singleton(); // 4 } } } return singleton; } }
可是,仍然有问题!!post
建立对象过程:性能
(1)分配内存空间优化
(2)初始化对象url
(3)将内存空间的地址赋值给对应的引用spa
(2)(3)会被处理器优化,发生重排序线程
举例:
A线程singleton = new Singleton()发生重排序,将分配的内存空间引用赋值给了静态属性singleton(即singleton != null),而对象还未初始化(即Integer a == null);
B线程此时调用getInstance()方法,由于singleton != null,直接返回singleton。当B线程使用singleton的a属性时就会空指针。
分析:
问题在于singleton = new Singleton()的重排序
(1)不容许初始化阶段步骤2 、3发生重排序。
(2)容许初始化阶段步骤2 、3发生重排序,可是不容许其余线程“看到”这个重排序。
解决:
一、利用volatile限制重排序
public class Singleton { private volatile static Singleton singleton;// 经过volatile关键字来确保安全 private Singleton(){} public static Singleton getInstance(){ if(singleton == null){ synchronized (Singleton.class){ if(singleton == null){ singleton = new Singleton(); } } } return singleton; } }
(1)分配内存空间
(2)初始化对象
(3)将内存空间的地址赋值给对应的引用
第(3)步 volatile修饰的变量singleton的写入操做,经过内存屏障限制的重排序 参考:Java并发(六):volatile的实现原理
二、利用类初始化
JVM会保证一个类的类构造器在多线程环境中被正确的加锁、同步,若是多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的类构造器,其余线程都须要阻塞等待,直到活动线程执行方法完毕。
特别须要注意的是,在这种情形下,其余线程虽然会被阻塞,但若是执行初始化的那条线程退出后,其余线程在唤醒以后不会再次进入/执行初始化,由于在同一个类加载器下,一个类型只会被初始化一次。
public class Singleton { private static class SingletonHolder{ public static Singleton singleton = new Singleton(); } public static Singleton getInstance(){ return SingletonHolder.singleton; } }