當DC遇到volatile,真不清楚要搞什麼?

public class Singleton {
    private volatile static Singleton instance;

    private Singleton(){}

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

先看這個單例模式,這就是所謂的 DCL,這裏重點不在說明什麼是DCL,這裏要說說,爲什麼 instance要用 volatile來修飾

volatile關鍵字有兩個作用

  1. 保證線程可見性
  2. 防止指令重排序
    在這裏插入圖片描述
    線程可見性,即線程A修改的某個變量,線程B讀取這個值,會讀到修改後的值,而不是本地緩存中的值。這裏緩存一致,通過兩個方法實現。總線加LOCK#鎖的方式(鎖總線) 或者 通過緩存一致性協議(鎖緩存),這兩個都是硬件方面的實現。加鎖的方式有性能問題,緩存一致性協議,比如MESI是這樣實現的。若某對象被volatitle修改後,主內存中就把它設置爲invalid狀態,線程B讀取時,發現是invalid狀態,就從主內在中重新讀取。

防止指令重排序, 是JVM層級,是通過內存屏障來實現的。並且是讀寫都有內存屏障
在這裏插入圖片描述
在這裏插入圖片描述
明白了這些,來說說DCL那個問題,爲什麼要用volatile關鍵字,就是防止指令重排序。1.對象創建先分配內存,2、再初始化相關數據,3、最後將引用指向堆內地址。其中第二個步驟會複雜的多,由於指令重排序的可能,最後一步可能先於第二步執行完成。

問題就來了,第一個線程進來調用,調用創建對象的方法,多線程情況下,其它進來的線程會阻塞。但創建對象的過程中,可能發生指令重排序,第3步先於第2步完成,這時候又有一個線程進來,調用第一個if決斷,對象不等於null,直接反一個半初始化的對象。這就是問題,雖然發生的概率很小。加上volatile,杜絕第3步先於第2步完成的情況,這樣就不可能返回一個半初始化的對象。

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