volatile+雙重檢查 實現單例模式

/*
   目標:雙重檢查機制,以及使用volatile修飾(最好,最安全,最推薦)
   步驟:
        1.構造器私有
        2.提供一個靜態變量用於存儲一個單例對象
        3.提供一個方法進行雙重檢查機制返回單例對象
        4.使用volatile修飾靜態的變量

    雙重檢查的優點:線程安全,延遲加載,效率較高!
 */
public class Singleton {
   
   
    private volatile static Singleton INSTANCE;
    private Singleton(){
   
   
        
    }
    public static Singleton getInstance(){
   
   
        // 第一次檢查:判斷單例對象的變量是否爲null
        if(INSTANCE == null){
   
   
            synchronized (Singleton.class){
   
   
                //第二次檢查:判斷單例對象的變量是否爲null
                if(INSTANCE == null){
   
   
                    INSTANCE = new Singleton();
                }
            }

        }
        return INSTANCE;
    }
}

爲什麼要使用volatile修飾呢?

  1. 禁止指令重排序
    創建對象要經過如下幾個步驟
    a. 分配內存空間
    b. 調用構造器,初始化實例
    c. 返回地址給引用



    但是JVM具有指令重排的特性,執行的順序有可能變成 a-c-b,指令重排在單線程下不會出現問題,但是在多線程下會導致一個線程獲得還沒有初始化的實例。例如:線程T1執行了a,b,此時線程T2調用getInstance()方法發現INSTANCE不爲null,因此返回INSTANCE,但此時INSTANCE還未被初始化。

  2. 保證可見性
    由於可見性問題,線程T1在自己的工作線程內創建了實例,但此時還未同步到主內存中,此時線程T2判斷INSTANCE還是null,那麼線程T2又將在自己的工作線程創建一個實例,這樣就創建了多個實例

    如果加上了volatile修飾INSTANCE之後,保證了可見性,一旦線程T1返回了實例,線程T2可以立即發現INSTANCE不爲null

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章