單例模式的雙重檢查


單例模式的雙重檢查

雙重檢查

public class Singletone{
    private static Instance instance;

    public Instance getInstance(){
        if(instance == null){
            synchronized(Singletone.class){
                if(instance == null){
                    instance = new Instance();
                }
            }
        }
        return instance;
    }
}

問題
instance = new Instance();是由三個步驟組成的:

  • 為對象分配內存
  • 實例化對象
  • 將引用指向對應的內存地址

但是第2,3步可能發生指令重排列,導致先將引用指向一個未實例化對象的內存地址,然后再進行實例化對象。
若此時第二個線程進行第一個非空判斷時,則為false,會直接返回還沒有實例化對象的內存地址,從而可能產生異常。

解決

  • 禁止指令重排列
  • 允許指令重排列,但是其他線程“不可見”

方案一:基於volatile禁止指令重排列

public class Singletone{
    private volatile static Instance instance;

    public Instance getInstance(){
        if(instance == null){
            synchronized(Singletone.class){
                if(instance == null){
                    instance = new Instance();
                }
            }
        }
        return instance;
    }
}

在成員變量中加入volatile變量,禁止使用new創建對象時的指令重排列。

方案二:基於類初始化的解決方案

public class Singletone{
    private static class InstanceHolder{
        public static Instance instance = new Instance();
    }

    public static Instance getInstance(){
        return InstanceHolder.instance;
    }
}

JVM在類初始化階段進行類的初始化。在初始化期間,JVM會獲取一個鎖,從而同步多個線程對同一個類的初始化。

第一個獲得鎖的線程會完成實例對象的創建,其他線程會直接獲取創建的實例對象。

參考:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM