2009-01-30 130 views
3

這似乎也許是一個天真的問題,但我和一位同事討論了一下,我認爲沒有真正需要緩存是線程安全的/同步的,因爲我會認爲誰並不重要正在提供一個價值,因爲給定密鑰的值應該是「不變的」(因爲它最終來自同一個來源)。如果這些值可以很容易地改變,那麼緩存本身似乎並不是很有用(因爲如果你關心這個值是「當前正確的」,你應該去原始的源)。緩存是否需要同步?

我看到至少使GET同步的主要原因是,如果在緩存中錯過非常昂貴並且您不希望多個線程每次都出去獲取將值放回緩存中。即使這樣,你也需要在讀取 - 讀取週期期間實際上阻止所有消費者的東西。

無論如何,我的工作假設是散列本質上是線程安全的,因爲對於任何{k​​ey,value}組合,值都是null或者是什麼,無論誰去那裏「first」來寫。

問題是:這是一個合理的假設嗎?

更新:我的問題的真正範圍是圍繞非常簡單的id-> value風格的緩存(或{參數} - > {計算值},無論誰寫入緩存,值將是相同的,我們只是試圖從「重新計算」/返回到數據庫中存儲,實際的對象圖形是不相關的,並且緩存通常是長期的。

回答

4

對於散列的大多數實現,喲我需要同步。如果哈希表需要擴展/重新編譯會怎樣?如果兩個線程試圖向密鑰不同的哈希表中添加內容,但哈希碰撞?他們都可以同時以不同的方式修改哈希表中的同一個槽。假設你正在使用哈希表來實現你的緩存(你暗示你的問題),我建議你閱讀一下關於hash tables are implemented如果你不熟悉這個的細節。

+0

非常好的一點...當重新生成桶時,它將使用它看作當前鍵/值的集合,並且如果兩個rehash()調用正在進行,則結果可能會很有趣。 謝謝! – 2009-01-30 23:27:40

1

只要獲取和釋放一個鎖小於重新創建對象的成本(從文件或數據庫或其他),所有對高速緩存的訪問都應該確實同步,如果不是,你根本不需要高速緩存:)

2

寫入並不總是原子的。您必須使用原子數據類型或提供一些同步(RCU,鎖等)。沒有共享數據本身是線程安全的。或者通過堅持無鎖算法(即儘可能可行)使其消失。

1

如果你想避免數據損壞,你必須同步。當緩存包含多個必須以原子方式更新的表時,尤其如此。想象一下你有一個DMV(機動車輛部門)的數據庫。您向數據庫中添加一個新人員,該人員將擁有自動註冊的記錄以及收到的有關家庭住址記錄和其他聯繫信息的記錄。如果您不以原子方式更新這些表 - 在緩存中的數據庫中 - 則任何將數據從緩存中提取出來的客戶端可能會得到不一致的數據。

是的,任何一塊數據可能是不變的,但數據庫通常保存的數據 - 如果不一起更新並原子化 - 可能導致數據庫客戶端獲得不正確或不完整或不一致的結果。

+0

我應該更多地討論我的問題。我指的是簡單對象的緩存緩存(id-> value)。如果有關於對象的「髒」的問題,那麼我正在考慮的緩存(LRU長壽命緩存,它不會從源獲取更新)是不合適的。 – 2009-01-30 23:32:11

0

如果您使用Java 5或更高版本,則可以使用ConcurrentHashMap。這以線程安全的方式支持多個讀者和作者。

+0

這是不夠的,如果緩存包含多個需要更新原子的表。 – Eddie 2009-01-30 20:40:41