你的單例模式真的是執行緒安全的嗎?

gejiajia1314發表於2018-10-10

在我們平時的專案中,單例模式是十分常見的設計模式,當然我們使用最多的是"懶漢式":

public class SingleTon {

    private static SingleTon instance = null;

    private SingleTon(){

    }

    public static SingleTon getInstance(){
        if(instance == null){                   // 1
            synchronized (SingleTon.class){
                if(instance == null){
                    instance = new SingleTon(); // 2
                }
            }
        }
        return instance;
    }
}

在初始化的時候我們使用雙重判斷的方式來確保只會被初始化一次,看上去很完美,但真的是這樣嗎?

答案是否定的,因為我們忽略了instance是否被初始化完畢.

我們先了解一下物件是如何在jvm中生成的,一共有三個步驟,

1.分配記憶體空間

2.初始化物件

3.設定instance指向剛分配的記憶體空間

因為第2步和第3步不存在資料依賴性,所以可能會被重排序。2和3重排序之後的執行順訊可能如下:

1.分配記憶體空間

2.設定instance指向剛分配的記憶體空間

3.初始化物件

 假設程式是按照第二種情況的例項化順序,那麼當兩個執行緒同時執行該程式是,就有可能其中一個執行緒拿到了未初始化完成的物件,這樣對我們程式的執行效率影響是比較嚴重的.那該如何避免重排序呢?

答案是使用volatile關鍵字修飾instance,這樣就能避免jvm對我們的程式進行重排序,從而解決該問題的發生

public class SingleTon {

    private volatile static SingleTon instance = null;

    private SingleTon(){

    }

    public static SingleTon getInstance(){
        if(instance == null){                   // 1
            synchronized (SingleTon.class){
                if(instance == null){
                    instance = new SingleTon(); // 2
                }
            }
        }
        return instance;
    }
}

 

相關文章