雙重檢測單例模式中使用volatile的原因


以前一直沒在意雙重檢測單例模式中volatile的作用,最近又注意到了它的細節處的作用,在這里記錄下。雖然現在單例模式的最佳選擇是使用枚舉,但通過這個增長知識也是不錯的。下面是一般的雙重檢測單例模式的代碼:

public class Single {
    private static volatile Single instance = null;

    public static Single getInstance(){         //1
        if (null == instance){                   //2
            synchronized (Single .class){       //3
                if (null == instance){             //4 
                    instance = new Single ();       //5
                }
            }
        }
        return instance;                        //6
    } 
}

看起來好像沒有必要使用volatile保證instance的可見性,因為  instance = new Single (); 這行是在synchronized里面的。但是這里的volatile並不是為了保證可見性的,而是為了防止指令重排造成返回的  instance 不正確的情況發生。

看line 5代碼: instance = new Single ();對象的創建實際包含一下幾步:

    1. 類是否已經加載,沒加載就加載類 ; 2. 申請內存; 3 初始化內存(置0,null等等);4. 執行構造方法,初始化對象; 5. 將生成的對象賦給引用

問題就在於這幾步指令是可能重排的,比如1 2 3 4 5,變成1 2 3 5 4。也就是說還沒有執行構造方法將這個對象的屬性初始化,各個屬性都是默認值,就將這個對象賦給了引用instance了。假設有個線程A發生了上面說的情況,生成對象時執行了1 2 3 5步,這時候有個線程B執行line2發現instance不為null了,於是直接執行line6將還未執行構造方法的對象返回。因此為了萬無一失,還是要使用volatile的,防止生成對象時第4步和第5步順序顛倒。


免責聲明!

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



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