2012-01-14 56 views

回答

21

因爲它是最終的,是的。最終變量具有特殊的線程安全語義,因爲其他線程可以保證在構造函數完成時至少能看到最終字段。

這是在JLS 17.5,雖然語言有點密集。這些語義是在Java 1.5中引入的,特別是由JSR-133引入的。關於JSR-133及其各種含義的非規範討論,請參見本頁。

請注意,如果您修改其構造函數後的實例,即而不是必然是線程安全的。在這種情況下,您必須採取通常的線程安全預防措施,以確保在邊緣發生之前發生。

我相當確定(儘管不是100%),事實上,只有一個線程的類初始化是而不是這裏的一個因素。確實,這個類只是由一個線程初始化的,但我不相信有任何具體的發生 - 在該線程之間建立邊之前,任何其他使用該類的線程(除了該其他線程不需要重新初始化班上)。因此,如果沒有關鍵字final,另一個線程將能夠看到該對象的部分構建的實例。具體發生在JMM定義的邊緣之前是JLS 17.4.5,並且類初始化沒有在那裏列出。

+0

是的,謝謝。好的答案 – MyTitle 2012-01-14 20:59:55

+0

我相信比final更重要的原因是類初始化(靜態初始化)必須同步並且線程安全。它使用同步建立了發生之前的排序。有關更多詳細信息,請參閱12.4.2(http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.4.2)。 – sjlee 2012-01-16 01:49:28

+0

@sjlee的確,初始化需要鎖,但是如果線程A初始化MyClass,並且線程B在5分鐘後出現並使用MyClass,那麼線程B將不會嘗試初始化MyClass(因爲它已經被初始化)。這意味着它不會獲得'MyClass.class'上的鎖定,這意味着線程A的動作和線程B之間沒有發生任何反應。如果每個線程在它使用它時都確實鎖定了'MyThread.class',那麼在關係之前,你永遠不需要建立任何其他的發生。 – yshavit 2012-01-16 03:01:14

2

任何類的靜態初始化塊都保證是單線程的。一個更簡單的單例是使用一個枚舉

enum Singleton { 
    INSTANCE; 
} 

這也是線程安全的,並且lazy-initialised類。

+0

對此的引用,例如JLS? – paislee 2012-01-14 20:26:51

+0

@paislee:我在JVMS的家裏比較多,所以我可以爲你提供一個[參考](http://java.sun.com/docs/books/jvms/second_edition/html/Concepts.doc。 HTML#24237)。如果你遵循這個過程,你會發現類初始化可能只能由一個線程完成(每個類加載器)。在步驟8中調用靜態初始化程序。 – musiKk 2012-01-14 20:33:05

+1

JVMS以各種方式提供比JLS更嚴格的語義,因此它不是一般語言正確性的重要來源。 JLS在[JLS 12.4]中定義了類初始化(http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.4),12.4.2詳細說明了該過程。 – yshavit 2012-01-14 20:44:28

3

它保證是線程安全的,因爲JVM保證靜態初始化器在單個線程上執行。

這並不意味着Foo的實例在內部是線程安全的 - 它只是意味着您可以保證Foo的構造函數將在一個線程上通過此特定代碼路徑被精確調用一次。

5

類構造函數和靜態/實例初始化保證要被自動執行,並且自private static final FOO INSTANCE = new FOO;相當於

private static final FOO INSTANCE; 

static{ 
    INSTANCE = new FOO(); 
} 

這種情況下落入上述類別。

相關問題