雙重檢查鎖定(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; } }