雙重檢查鎖定(Double Check Lock,DCL)
1、懶漢式單例模式,無法保證線程安全:
public class Singleton { private static Singleton singleton; private Singleton() { } public static Singleton getInstance() { if (singleton == null) {// 多個線程同時執行到此,會生成多個Singleton實例 singleton = new Singleton(); } return singleton; } }
2、同步處理,synchronized就會導致這個方法比較低效:
public class Singleton { private static Singleton singleton; private Singleton() {} public static synchronized Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } }
3、雙重檢查 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; } }
但是,仍然有問題!!
創建對象過程:
(1)分配內存空間
(2)初始化對象
(3)將內存空間的地址賦值給對應的引用
(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發生重排序,但是不允許其他線程“看到”這個重排序。
解決:
1、利用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的實現原理
2、利用類初始化
JVM會保證一個類的類構造器在多線程環境中被正確的加鎖、同步,如果多個線程同時去初始化一個類,那么只會有一個線程去執行這個類的類構造器,其他線程都需要阻塞等待,直到活動線程執行方法完畢。
特別需要注意的是,在這種情形下,其他線程雖然會被阻塞,但如果執行初始化的那條線程退出后,其他線程在喚醒之后不會再次進入/執行初始化,因為在同一個類加載器下,一個類型只會被初始化一次。
public class Singleton { private static class SingletonHolder{ public static Singleton singleton = new Singleton(); } public static Singleton getInstance(){ return SingletonHolder.singleton; } }