簡體   English   中英

雙重檢查鎖定而不使用volatile-keyword並且不同步整個getInstance()方法

[英]Double checked locking without using volatile-keyword and without synchronizing the entire getInstance() method

以下是我的單例類,我使用雙重檢查鎖定而不使用volatile關鍵字而不同步整個getInstance()方法:

public class MySingleton {

    private static MySingleton mySingleton;

    public static MySingleton getInstance() {
        if(mySingleton == null) {
            synchronized(MySingleton.class) {
                if(mySingleton == null) {
                    MySingleton temp = new MySingleton();
                    mySingleton = temp;
                }
            }
        }

        return mySingleton;
    }
}

據我說,這是線程安全的。 如果有人認為,這不是線程安全的,有人可以詳細說明為什么他/她認為這不是線程安全的嗎? 謝謝。

這里我們有一個synchronized寫入和一個non-synchronized讀取。 我們無法保證讀取將獲得寫入設置的值。 所以這段代碼很麻煩。 但這是一個非常微妙的錯誤,特別是在多核CPU中。

如果由於某種原因需要繼續使用上面的代碼,一種解決方案是將變量聲明為volatile如下所示:

private static volatile MySingleton mySingleton;

或者使變量的讀取synchronized 它們中的任何一個都會以性能為代價來解決您的問題。

另一個重點是你的temp變量在這里沒用。 它只是一個冗余的代碼,什么都不做。 而是直接將它分配給你的mySingleton變量,如下所示,

mySingleton = new MySingleton();

通過使用enum類型來制作單例,可以非常容易地解決所有上述問題。 由於枚舉本質上是Serializable ,因此我們不需要使用Serializable接口來實現它。 反思問題也不存在。 因此,100%保證在JVM中只存在一個Singleton實例。 因此,建議將此方法作為在Java中制作單例的最佳方法。

在我閱讀所有評論之前,我不知道這個問題。 問題是各種優化過程(編譯器,熱點,無論如何)都會重寫代碼。 您的“臨時”解決方案很容易被刪除。 我發現很難相信構造函數可以返回一個部分對象,但如果知識淵博的貢獻者這樣說,我會相信他們的意見。

是的,但我使用的是“臨時”變量。 它不解決“部分創建對象”問題嗎?

不,不是的。

假設一些線程A調用getInstance() ,最后創建一個新實例並分配mySingleton變量。 然后線程T出現,調用getInstance()並看到mySingleton不為null

此時,線程T沒有使用任何同步。 如果沒有同步,Java語言規范(JLS)不要求線程T以與線程A相同的順序查看線程A所做的分配。

我們假設單例對象有一些成員變量。 線程A顯然必須在將引用存儲到mySingleton之前初始化這些變量。 但是JLS允許線程T看到mySingleton != null但仍然看到成員變量處於未初始化狀態。 在一些多核平台上,它實際上可以這樣發生。

首先將對象引用分配給本地temp變量不會改變任何內容。 實際上,正如Steve11235所指出的那樣, temp變量甚至可能實際上不存在於字節代碼或本機指令中,因為Java編譯器或熱點編譯器可以完全優化它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM