2011-03-15 81 views
18

我有一個緩存,我使用simeple HashMap實現。像 -Java多線程原子參考分配

HashMap<String,String> cache = new HashMap<String,String>(); 

此緩存大部分時間用於從中讀取值。我有另一種重新加載緩存的方法,並在這個方法裏面我基本上創建一個新的緩存,然後分配引用。據我所知,對象引用的賦值是Java中的Atomic。

public class myClass { 
    private HashMap<String,String> cache = null; 
    public void init() { 
     refreshCache(); 
    } 
    // this method can be called occasionally to update the cache. 
    public void refreshCache() { 
     HashMap<String,String> newcache = new HashMap<String,String>(); 
     // code to fill up the new cache 
     // and then finally 
     cache = newcache; //assign the old cache to the new one in Atomic way 
    } 
} 

我明白,如果我不申報緩存中揮發,其他線程將無法看到的變化,但它不是時間關鍵我使用的情況下傳播的高速緩存給其他線程,它們的變化可以繼續使用舊緩存以延長時間。

您是否看到任何線程問題?考慮到許多線程正在從緩存中讀取數據,並且只有在緩存重新載入的時候。

編輯- 我的主要困惑是我不必在這裏使用AtomicReference作爲賦值操作本身是原子嗎?

編輯 - 我明白,要使排序正確,我應該將緩存標記爲volatile。 但是,如果refreshCache方法標記爲synchronized,那麼我不必將緩存設置爲volatile,因爲Synchronized塊會照顧排序和可見性?

+0

爲了簡單的緩存目的,我通常使用* ConcurrentHashMap *並且不用擔心鎖定。我通常不關心一個(引用透明的)計算是否發生兩次(也就是說,因爲線程B開始計算與線程A當前正在計算的值相同的值,因此尚未放入緩存)。在你的情況下,我還會讓* cache *成爲* volatile *。但我沒有看到「刷新緩存」的重點。使你的緩存成爲一個LRU/MRU,並簡單地向它添加新的緩存值,而不是「重置」它。 – SyntaxT3rr0r 2011-03-15 05:32:15

+0

至少使用新映射刷新緩存允許使用非阻塞緩存映射實現 – ThomasRS 2012-04-19 11:31:52

回答

27

這是不是安全沒有適當的內存屏障。

有人會認爲緩存(緩存= newCache)的分配會發生在填充緩存的步驟之後。但是,其他線程可能會因這些語句的重新排序而受到影響,以便在填充緩存之前分配可能會發生。因此,可以在完全構建之前獲取新緩存,或者甚至可以看到ConcurrentModificationException。

您需要強制執行happen-before關係來防止這種重新排序,並且將緩存聲明爲volatile將實現此目的。

+1

這是一個很好的建議。我有這個問題,並感謝清除它。如果訂購被修改,那麼它肯定是一場災難。 – Shamik 2011-03-15 15:19:37

+0

如果我使refreshCache成爲同步方法,會發生什麼情況? – Shamik 2011-03-15 19:26:57

+0

如果要確保只有一個線程可以在給定時間更改數據,那麼您可能希望使其同步。但是,我假設讀者線程不會參與鎖定(他們通過不同的方法來讀取值),在這種情況下,同步本身並不會給您提供保護。使緩存字段變爲易失性是實現事前 - 事前關係的有效方式。 – sjlee 2011-03-15 19:37:48

-1

似乎沒問題。請確保refreshCache不會過於頻繁地被調用或標記爲​​。

4

您應該將緩存標記爲volatile

雖然您注意到其他線程可能會繼續使用陳舊緩存「很長一段時間」,但您應該注意,沒有同步邊緣,它們可能會永遠繼續使用陳舊緩存。這可能不是期望的行爲。

在優選方案(主要是由於可讀性)的順序:

  • 更新的同步方法
  • 使用AtomicReference<Map<>>
  • 使用volatile

參見本question領域。

0

CopyOnWrite怎麼樣..收藏:

java.util.concurrent.CopyOnWriteArraySet 
java.util.concurrent.CopyOnWriteArraySet 
and org.apache.mina.util.CopyOnWriteMap 

它們可以很好地匹配你的情況,它們是線程安全的。