單例模式 雙重檢查判斷 不一定安全
雙重檢查判斷
public class Singleton {
private static Singleton instance=null;
private Singleton(){
}
public static Singleton getInstance() {
if(instance==null){
synchronized (Singleton.class){
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
1)當線程A、B同時調用getInstance()方法,他們同時發現 instance == null成立(檢查判斷 1),同時去獲取的Singleton.class鎖
2)其中線程A獲取到鎖,線程B 處於等待狀態;線程A會創建一個SingleTon實例,之後釋 放鎖
3)線程A釋放鎖後,線程B 被喚醒,線程B獲取到鎖,然後線程B檢查
instance == null 不成立(檢查判斷2),不會再創建Singleton實例對象
編譯優化後異常
我們認爲的new Singleton()操作
- 1)分配內存地址 M
- 2)在內存 M 上初始化Singleton 對象
- 3)將M的地址賦值給 instance 對象
JVM編譯優化後可能的new Singleton()操作
- 1)分配內存地址 M
- 2)將M的地址賦值給instance變量
- 3)在內存M上初始化 Singleton 對象
異常發生過程(如上圖,JVM創建new Instance()對象時先賦值再初始化)
1)線程A先執行getInstance()方法,當線程A在執行完變量的內存地址賦值(尚未初始化)時,發生線程切換,線程B獲得CPU的執行權
2)線程B在執行第一個判斷,發現 instance == null條件不成立,直接返回instance,但此時instance並沒有初始化,此時訪問instance對象的成員變量就可能發生空指針異常
解決
上述問題出現的本質原因是,JVM在編譯時的執行重排序造成的,所以只要禁止指令重排序,就可以解決這個問題,所以需要在Singleton對象的成員變量instance前加volatile關鍵字
private volatile static Singleton instance;