2010-09-08 50 views
3

問題來了,當我看到這個代碼:使用java.util.concurrent.Concurrent *容器提供的volatile時會發生什麼?

private static volatile ConcurrentHashMap<String, String> cMap = null; 
static { 
    cMap = new ConcurrentHashMap<String, String>(); 
} 

對我來說,它看起來像揮發性有多餘的,因爲容器是ConcurrentHashMap其根據JavaDoc已經同步放,DUH的類使用cMap只實例化它一次,並沒有任何設置或獲取它的方法。

我看到揮發性此處提供的唯一的事情是,如果我將設置CMAP在不久的將來,引用新的對象,這些讀取和寫入操作將被同步。

我錯過了什麼嗎?

回答

7

volatile修改沒有任何參與類的事 - 它只是與變量cMap做。它隻影響線程獲取或更改該變量值的方式。當你在引用的對象上調用方法時,你已經超出了volatile的範圍。如你所說,它基本上確保所有線程都能保證看到cMap值的變化(即使其指向不同的地圖)。

可能是一個好主意 - 或者它可能不是,這取決於其他代碼的作用。如果你可以把它final例如,你不會需要它volatile ......

+0

是的,使它最終是有道理的。 – Bleadof 2010-09-08 14:25:30

1

只有當其值發生更改時,纔會聲明cMap是易失性的。聲明它揮發性說什麼關於在地圖上舉行的對象。

如果cMap更改所有線程將需要查看CHM的新的最新值。這就是說,我強烈建議cMap是最終的。非最終的靜態變量可能很危險。

2

聲明的 cMap引用作爲 volatile確保其初始值是所有線程可見。沒有這個AFAIK,它不能保證,即一些線程可能會看到一個 null引用,而不是對正確初始化的映射對象的引用。

[更新]作爲@irreputable指出的(並且是用Java併發實踐解釋說,第3.5.3節),我錯了上述說法:靜態初始化通過在類中的JVM執行確初始化時間,由JVM的內部同步保護。所以volatile是沒有必要的。 [/更新]

OTOH聲明它final(和初始化它馬上,而不是在一個單獨的static塊)將保證可視性太。

請注意,引用是volatile或不與它所引用的變量的類別無關。即使被引用的類是線程安全的,引用本身也可能不是。

+0

我認爲你錯了,看我的答案。 – irreputable 2010-09-08 18:21:54

+0

@irreputable,哎呀,你是對的。我的不好:-(固定和爲你+1 :-) – 2010-09-09 08:24:41

5

除非變量稍後重新分配,否則volatile是完全不必要的。

任何寫操作發生在靜態初始化是可見的使用類的任何代碼(即當一個靜態方法/域被訪問,當一個構造函數被調用)

沒有這樣的保證,我們是在深陷困境。數百萬行代碼將是錯誤的。

看到JLS3 $ 12.4.2:

用於初始化類 或接口的步驟然後如下:

  1. 同步(§14.19)代表類的類對象上 或界面進行初始化。