2016-10-06 38 views
5

讓我們以SimpleDateFormat爲例,因爲它不是線程安全的。java中的volatile和threadLocal

我可以讓每個線程使用的ThreadLocal這樣它自己的SimpleDateFormat的副本有:

private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){ 
    @Override 
    protected SimpleDateFormat initialValue() 
    { 
     return new SimpleDateFormat("yyyyMMdd HHmm"); 
    } 
}; 

但volatile關鍵字保證一個線程將具有最新的變量的副本。所以我可以不這樣做:

volatile SimpleDateFormat myformatter; 

並實現相同的線程安全?

+1

因爲這不是線程安全問題發生的地方:線程安全問題是'SimpleDateFormat'具有可變狀態,這與引用是否存儲在易失性字段中無關。 –

+1

線程局部變量和volatile變量不一樣!對於線程本地,每個線程都有一個單獨的變量副本。使用volatile,所有線程共享一個變量。像Andy提到的那樣,使變量volatile成爲線程安全的是因爲SimpleDateFormat具有不能由多個線程同時更新的內部狀態。 – Jesper

回答

4

volatile關鍵字保證一個線程將具有最新的可變

的volatile變量只有,並非其字段的副本。如果您需要更改變量的值,則volatile僅適用。在你的使用情況,final看起來會更合適:

private static final SimpleDateFormat format = ... 

這也保證,你將有最新的變量的值 - 因爲它只能分配一次它的價值,並static final有保障一旦這個類被完全加載,這個可見性就會被釋放


但是,這不是爲什麼SimpleDateFormat不是線程安全反正理由:它有它用來存儲中間值格式的日期時可變狀態。

如果一個線程調用format而另一個也是在同一SimpleDateFormatter實例format方法,這些中間變量得到不可預知的跺着腳,導致線程之間的干擾,因此不可預知的輸出。

這些中間變量的值在由另一個線程讀取/寫入時是否是最新的並不重要 - 它們的更新可以是散佈的。

簡而言之,volatile不會阻止線程干擾,因此不適用於這裏的ThreadLocal

+1

'只在volatile變量中,而不是它的字段。「---這實際上是不精確的,甚至是誤導性的,因爲你會觀察寫入到volatile字段之前寫入線程所做的所有對被引用對象字段的寫入。這是安全出版的本質。 –

+0

@MarkoTopolnik真的嗎?我想到的東西就像'System.out.println(volatileField.nonVolatileField);'''volatileField''的讀取可能會讓你知道直到那個時刻所有對'nonVolatileField'的寫入;但不能'nonVolatileField'然後在讀'volatileField'和'volatileField.nonVolatileField'之間更新? –

+0

是的,這就是爲什麼我說你的措詞是「誤導」而不是「不正確」。這聽起來像你沒有_any_保證除了'volatile'引用本身。另一方面,你不能保證觀察volatile字段的「最新」狀態,因爲「最近的」在JMM中甚至不是一個明確定義的概念。本質區別在於順序一致性與線性化。 –

0

由於SimpleDateFormat的相同實例將在不同線程中使用,因此您無法實現與volatile相同的線程安全。

1

ThreadLocal是一個使線程擁有自己本地副本的對象的設施。 ThreadLocals最適用於線程安全的線程安全策略線程約束(即使對於非線程安全的多個對象,線程安全的使用仍然是可能的,只要沒有引用它們就可以泄漏出來限制線程)。對於在實例化它們的線程之外共享的可變對象,ThreadLocals不能幫助線程安全地使用它。

volatile關鍵字用於提供線程安全性的弱形式,其中包含可以被許多不同線程訪問的變量。一個關鍵的區別是ThreadLocals通常不會被多個線程訪問。從廣義上講,線程安全需要可見性(變量的最新更新應該對其他線程可見)和互斥(狀態轉換必須是原子的,以便狀態不能被觀察爲不一致)。 Volatile與Java內存模型一起工作以保證變量可見,但它不提供互斥形式,因此不提供對象中狀態轉換的原子性。

因爲volatileThreadLocal是如此不同,所以實際上沒有什麼可以替代另一個的普通情況。