閱讀這個網站,我發現this:爲什麼這個靜態最終變量在單線程中是線程安全的?
類時實際使用時,纔會執行行
private static final Foo INSTANCE = new Foo();
[中],這需要懶惰實例化的關懷,是保證是線程安全的。
爲什麼這保證是線程安全的?因爲這個字段是final?或者出於其他原因?
閱讀這個網站,我發現this:爲什麼這個靜態最終變量在單線程中是線程安全的?
類時實際使用時,纔會執行行
private static final Foo INSTANCE = new Foo();
[中],這需要懶惰實例化的關懷,是保證是線程安全的。
爲什麼這保證是線程安全的?因爲這個字段是final?或者出於其他原因?
因爲它是最終的,是的。最終變量具有特殊的線程安全語義,因爲其他線程可以保證在構造函數完成時至少能看到最終字段。
這是在JLS 17.5,雖然語言有點密集。這些語義是在Java 1.5中引入的,特別是由JSR-133引入的。關於JSR-133及其各種含義的非規範討論,請參見本頁。
請注意,如果您修改其構造函數後的實例,即而不是必然是線程安全的。在這種情況下,您必須採取通常的線程安全預防措施,以確保在邊緣發生之前發生。
我相當確定(儘管不是100%),事實上,只有一個線程的類初始化是而不是這裏的一個因素。確實,這個類只是由一個線程初始化的,但我不相信有任何具體的發生 - 在該線程之間建立邊之前,任何其他使用該類的線程(除了該其他線程不需要重新初始化班上)。因此,如果沒有關鍵字final
,另一個線程將能夠看到該對象的部分構建的實例。具體發生在JMM定義的邊緣之前是JLS 17.4.5,並且類初始化沒有在那裏列出。
任何類的靜態初始化塊都保證是單線程的。一個更簡單的單例是使用一個枚舉
enum Singleton {
INSTANCE;
}
這也是線程安全的,並且lazy-initialised類。
對此的引用,例如JLS? – paislee 2012-01-14 20:26:51
@paislee:我在JVMS的家裏比較多,所以我可以爲你提供一個[參考](http://java.sun.com/docs/books/jvms/second_edition/html/Concepts.doc。 HTML#24237)。如果你遵循這個過程,你會發現類初始化可能只能由一個線程完成(每個類加載器)。在步驟8中調用靜態初始化程序。 – musiKk 2012-01-14 20:33:05
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
它保證是線程安全的,因爲JVM保證靜態初始化器在單個線程上執行。
這並不意味着Foo的實例在內部是線程安全的 - 它只是意味着您可以保證Foo的構造函數將在一個線程上通過此特定代碼路徑被精確調用一次。
類構造函數和靜態/實例初始化保證要被自動執行,並且自private static final FOO INSTANCE = new FOO;
相當於
private static final FOO INSTANCE;
static{
INSTANCE = new FOO();
}
這種情況下落入上述類別。
是的,謝謝。好的答案 – MyTitle 2012-01-14 20:59:55
我相信比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
@sjlee的確,初始化需要鎖,但是如果線程A初始化MyClass,並且線程B在5分鐘後出現並使用MyClass,那麼線程B將不會嘗試初始化MyClass(因爲它已經被初始化)。這意味着它不會獲得'MyClass.class'上的鎖定,這意味着線程A的動作和線程B之間沒有發生任何反應。如果每個線程在它使用它時都確實鎖定了'MyThread.class',那麼在關係之前,你永遠不需要建立任何其他的發生。 – yshavit 2012-01-16 03:01:14