2011-07-07 60 views
0

任何人都可以解釋下面的代碼如何在多線程環境中正常工作,特別是當它不使用synchronized關鍵字時?單線程在多線程中不使用同步關鍵字

public class Singleton { 
private Singleton() {} 

private static class SingletonHolder { 
    private static final Singleton INSTANCE = new Singleton(); 
} 

public static Singleton getInstance() { 
    return SingletonHolder.INSTANCE; 
} 
} 
+0

@leppie,這似乎有點苛刻...,尤其是對於相對新手(雖然我不否認你正確的做到這一點,我們的票是我們自己做的,因爲我們認爲合適的 - 「我不同意你說的話,但我會爲你的死亡權利而鬥爭」以及所有爵士樂)。我認爲這些選票適用於Q/A本身,而不是發佈人員的某種獎勵/懲罰機制。由於您實際上是通過添加標籤來修復問題的,因此不能認爲問題不足。 IMOBIBWBJAMW(在我看來,但我以前錯了 - 只是問我的妻子):-) – paxdiablo

回答

1

爲異步單身人士可以工作得很好,在線程環境提供他們在一個線程中實例化之前其他線程試圖使用它們。

這可能就像在啓動任何其他線程之前從主線程調用getInstance()一樣簡單。

但是,這在這種特殊情況下無關緊要。由於您的實例變量是static final,這意味着它將在最初加載類時構建。通過調用getInstance(),類加載器引入該類,並在作爲其一部分構建INSTANCE成員之前,允許通過getInstance()進行調用。

類加載器本身具有鎖定機制以防止多線程併發執行,因此所有對getInstance()(包括第一個,緊隨該類加載之後)的調用都將返回已經初始化的值。

+0

當多個線程試圖調用Signleton.getInstance()時,上面的代碼提供了唯一的實例,而不是彼此共享該實例。現在,我想知道當沒有同步關鍵字時它會發生什麼?因爲即使多個線程可以同時調用SingletonHolder,那麼在這裏怎麼會沒有任何同步問題呢? – Mike

+0

@Mike,他們不提供獨特的實例。有一個_one_實例很早就構建完成,每次調用getInstance都會返回該實例。 – paxdiablo

+0

那麼,這是否意味着當2個線程同時調用getInstance時,它會創建它們的不同實例? – Mike

0

至於前期初始化,不需要使用同步關鍵字,因爲您靜態地初始化您的實例,幾乎在每種情況下都是因爲兩個原因而首選的方法。您可以在應用程序的運行時間內分攤昂貴的初始化成本,其中兩個(對您來說尤其重要)是它不需要同步,因此您可以避免多毛的雙重檢查鎖定方案。

如果您選擇使用延遲初始化,那麼您需要確保整個初始化過程是同步的,否則在多處理器環境中您會冒着創建兩個單例實例的風險。 Double Checked Locking And Why its a problem

初始化後,您的代碼將在多線程環境中正常工作。需要注意的是,您在所有線程中共享一個實例,因此您需要確保您的INSTANCE是線程安全的。有兩種方法可以做到這一點。首先,您可以確保您的所有成員都是私有的,並且可以訪問更改INSTANCE's狀態的方法,使用​​或包含發生狀態修改的同步塊。

實現線程安全性的第二種也許更好的方法是預先初始化您的實例,並在其初始化後使其不可變。不可變對象可以在線程間自由共享,而無需同步。閱讀Java實踐文章以獲取有關不可變對象以及如何創建它們的更多信息。

Java Practices: Immutable objects