[英]ConcurrentHashMap and compound operations
Hashtable和Collections.synchronizedMap是線程安全的,但仍然是復合操作
if (!map_obj.containsKey(key)) {
map_obj.put(key, value);
}
需要外部同步:
synchronized(map_obj) {
if (!map_obj.containsKey(key)) {
map_obj.put(key, value);
}
}
假設我們有ConcurrentHashMap(CHM)而不是Hashtable或HashMap。 CHM為上述復合操作提供了另一種putIfAbsent()
方法,因此無需外部同步。
但是假設CHM沒有提供putIfAbsent()
。 然后我們可以寫下面的代碼:
synchronized(concurrenthashmap_obj) {
if (!concurrenthashmap_obj.containsKey(key)) {
concurrenthashmap_obj.put(key, value);
}
}
我的意思是我們可以在CHM對象上使用外部同步嗎?它會起作用嗎?
對於上面的復合操作,在CHM中有putIfAbsent()
方法,但是如果我們使用CHM,我們如何為其他復合操作實現線程安全性。 我的意思是我們可以在CHM對象上使用外部同步嗎?
不,您不能使用外部同步來確保復合操作在ConcurrentHashMap
原子性。
確切地說,您可以使用外部同步來確保復合操作的原子性,但是只有當ConcurrentHashMap
所有操作ConcurrentHashMap
在同一個鎖上同步時(盡管在這種情況下使用ConcurrentHashMap
沒有意義 - 您可以用常規HashMap
)。
具有外部同步的方法僅適用於Hashtable
和Collections.synchronizedMap()
因為它們保證它們的基本操作也在這些對象上synchronized
。 由於ConcurrentHashMap
不提供這樣的保證,原始操作可能會干擾復合操作的執行,從而破壞其原子性。
但是, ConcurrentHashMap
提供了許多可用於以樂觀方式實現復合操作的方法:
putIfAbsent(key, value)
remove(key, value)
replace(key, value)
replace(key, oldValue, newValue)
您可以使用這些操作來實現某些復合操作,而無需進行明確的同步,就像對AtomicReference
,等等。
沒有任何理由你不能。 傳統同步適用於所有內容,沒有針對它們的特殊例外。 ConcurrentHashMaps只是使用更優化的線程安全機制,如果你想做一些更復雜的事情,回到傳統的同步可能實際上是你唯一的選擇(那和使用鎖)。
您始終可以使用synchronized
塊。 java.util.concurrent
的花哨集合不會禁止它,它們只是使它成為大多數常見用例的冗余。 如果您正在執行復合操作(例如 - 您要插入兩個必須始終具有相同值的鍵),則不僅可以使用外部同步 - 您必須這樣做 。
例如:
String key1 = getKeyFromSomewhere();
String key2 = getKeyFromSomewhereElse();
String value = getValue();
// We want to put two pairs in the map - [key1, value] and [key2, value]
// and be sure that in any point in time both key1 and key2 have the same
// value
synchronized(concurrenthashmap_obj) {
concurrenthashmap_obj.put(key1, value);
// without external syncronoziation, key1's value may have already been
// overwritten from a different thread!
concurrenthashmap_obj.put(key2, value);
}
由於ConcurrentHashMap實現了Map接口,它確實支持每個基本Map都具有的所有功能。 所以是的:你可以像任何其他地圖一樣使用它並忽略所有額外的功能。 但是你基本上會有一個較慢的HashMap。
同步Map和並發Map之間的主要區別在於 - 如名稱所示 - 並發。 想象一下,你有100個想要從Map讀取的線程,如果你synchronize
你就可以阻止99個線程,1個可以完成工作。 如果您使用並發100個線程可以同時工作。
現在,如果你考慮使用線程的實際原因,你很快就會得出結論,你應該擺脫所有可能的synchronized
塊。
這完全取決於“其他復合操作”和“工作”的含義。 同步與ConcurrentHashMap的工作方式與其他任何對象的工作方式完全相同。
因此,如果您希望將某些復雜的共享狀態更改視為原子更改,則必須在同一鎖上同步對此共享狀態的所有訪問。 這個鎖可以是Map本身,也可以是另一個對象。
java.util.concurrent.ConcurrentHashMap
“在依賴於線程安全但不依賴於其同步細節的程序中,Hashtable可與Hashtable完全互操作:它們不會拋出ConcurrentModificationException。”
“允許更新操作之間的並發”
一般來說,從同步角度來看,讀取是安全的,但不是內存的立場。
另請參見“ http://www.ibm.com/developerworks/java/library/j-jtp03304/ ”。
因此,應使用synchronizaton
和volatile
來管理並發讀取(與寫入相比)。
putIfAbsent
是你的朋友 :
如果指定的鍵尚未與值關聯,請將其與給定鍵關聯
value. This is equivalent to if (!map.containsKey(key)) return map.put(key, value); else return map.get(key);
除了動作執行!!!原子!!!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.