設計模式(二):單例模式(DCL及解決辦法)


public class Singleton {
    //懶漢模式   雙重檢查鎖定DCL(double-checked locking)
    //缺點:由於jvm存在亂序執行功能,DCL也會出現線程不安全的情況。(DCL失效問題)

    // jdk1.6及之后,只要定義為private volatile static SingleTon instance 就可解決DCL失效問題。
    // volatile確保instance每次均在主內存中讀取,這樣雖然會犧牲一點效率,但也無傷大雅。
    // volatile可以保證即使java虛擬機對代碼執行了指令重排序,也會保證它的正確性。
    private volatile static Singleton instance;  //延遲加載,需要時才創建實例

    private Singleton() {  //私有化構造函數
    } 

    public static Singleton getInstance() {
        if (instance == null) { //只有第一次調用時,才需要同步判斷(此時instance未初始化,為null)。一旦instance初始化完成,就不需要了
            synchronized (Singleton.class) {  //只要鎖住new即可,不需要放在外面getInstance()方法上
                if (instance == null) {  //二次判斷,為了避免線程一在創建實例之后,線程二進來也創建了新實例
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  
DCL及解決辦法 說明:
針對延遲加載法的同步實現所產生的性能低的問題,可以采用DCL,即雙重檢查加鎖(Double Check Lock)的方法來避免每次調用getInstance()方法時都同步。

Double-Checked Locking看起來是非常完美的。但是很遺憾,根據Java的語言規范,上面的代碼是不可靠的。
出現上述問題, 最重要的2個原因如下:
1, 編譯器優化了程序指令, 以加快cpu處理速度.
  2, 多核cpu動態調整指令順序, 以加快並行運算能力.

問題出現的順序:
1, 線程A, 發現對象未實例化, 准備開始實例化
  2, 由於編譯器優化了程序指令, 允許對象在構造函數未調用完前, 將共享變量的引用指向部分構造的對象, 雖然對象未完全實例化, 但已經不為null了.
  3, 線程B, 發現部分構造的對象已不是null, 則直接返回了該對象.

解決辦法:
可以將instance聲明為volatile,即 private volatile static Singleton instance
在線程B讀一個volatile變量后,線程A在寫這個volatile變量之前,所有可見的共享變量的值都將立即變得對線程B可見。

更深入理解DCL原理,請查看:Java內存模型 https://blog.csdn.net/wuzhiwei549/article/details/80006533?utm_source=copy


免責聲明!

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



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