我讀過這篇文章,聲稱使用靜態成員與java 1.5是線程安全的。
更具體地講,在Java Memory Model FAQ特別說:
如果某個字段在靜態初始化設置,可以保證它是可見的,正確的,以訪問任何線程該類
在您發佈的代碼,你static
字段是靜態初始化不集。如果做了以下那麼就不會有一個線程安全問題:
private static Foo foo = new Foo();
然而,在其他大多數情況下與static
領域的線程安全性問題:
換句話說,不要將在另一個線程可能能夠看到的地方引用正在構建的對象;請勿將其分配給靜態字段,請勿將其註冊爲任何其他對象的偵聽器,依此類推。
你的其他問題:
我想所有的線程會得到同樣的 「初始化」 富
沒有關於保證。兩個線程可以同時調用getFoo()
,肯定會得到Foo
的不同實例。
但是foo會被初始化多少次?
這是未知的,取決於有多少線程在運行,數據發佈等。
如果我將添加揮發性和foo定義爲「公共靜態揮發富......」它會改變什麼嗎?
這將確保至少Foo
會當它被分配到靜態字段,但它絕不再保護你當多個線程在同一時間撥打getFoo()
這種情況發生的競態條件妥善建造。
通常,當Foo
的構造便宜時,我將使用volatile static
字段模式,並且我不介意其中一個線程使用其實例Foo
,然後進行垃圾回收。我只希望剩下的大部分操作都使用同一個實例。
另一種模式是做類似如下:
private static final AtomicReference<Foo> fooRef = new AtomicReference<Foo>();
public static String getFoo() {
Foo foo = fooRef.get();
if (foo != null) {
return foo;
}
foo = new Foo();
if (fooRef.compareAndSet(null, foo)) {
return foo;
} else {
// foo ref was set by another thread so our Foo is not used
return fooRef.get();
}
}
這可能會創建,然後扔掉的Foo
一對額外的實例,但至少所有線程將使用的Foo
相同的實例。
但是,如果必須有且僅有一個Foo
實例,並且您不能在靜態初始化程序中構建它(請參閱上文),那麼您將被迫鎖定其構造。
在'Foo'的構造函數中放入一個日誌語句並嘗試。 –