簡體   English   中英

ConcurrentHashMap和復合操作

[英]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 )。

具有外部同步的方法僅適用於HashtableCollections.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。”

  • “允許更新操作之間的並發”

關於java內存

一般來說,從同步角度來看,讀取是安全的,但不是內存的立場。

另請參見“ http://www.ibm.com/developerworks/java/library/j-jtp03304/ ”。

因此,應使用synchronizatonvolatile來管理並發讀取(與寫入相比)。

關於putIfAbsent

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM