ConcurrentHashMap和复合操作

时间:2014-02-09 11:13:04

标签: java collections thread-safety

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对象上使用外部同步吗?

6 个答案:

答案 0 :(得分:17)

不,您不能使用外部同步来确保复合操作的原子性超过ConcurrentHashMap

准确地说,您可以使用外部同步来确保复合操作的原子性,但前提是ConcurrentHashMap的所有操作都在同一个锁上同步(尽管使用ConcurrentHashMap不会在这种情况下有意义 - 您可以用常规HashMap替换它。

使用外部同步的方法仅适用于HashtableCollections.synchronizedMap(),因为它们保证它们的原始操作对synchronized也适用于这些对象。由于ConcurrentHashMap没有提供这样的保证,原始操作可能会干扰复合操作的执行,从而破坏其原子性。

但是,ConcurrentHashMap提供了许多可用于以乐观方式实现复合操作的方法:

  • putIfAbsent(key, value)
  • remove(key, value)
  • replace(key, value)
  • replace(key, oldValue, newValue)

您可以使用这些操作来实现某些复合操作,而无需进行明确的同步,就像您对AtomicReference所做的那样,等等。

答案 1 :(得分:6)

没有任何理由你不能。传统同步适用于所有内容,没有针对它们的特殊例外。 ConcurrentHashMaps只是使用更优化的线程安全机制,如果你想做更复杂的事情,回退到传统的同步可能实际上是你唯一的选择(那和使用锁)。

答案 2 :(得分:6)

您始终可以使用synchronized块。 java.util.concurrent中的花哨集合并没有禁止它,它们只是使它成为大多数常见用例的冗余。如果您正在执行复合操作(例如 - 您要插入两个必须始终具有相同值的键),不仅可以您使用外部同步 - 您必须

E.g:

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);
}

答案 3 :(得分:3)

当ConcurrentHashMap实现Map接口时,它确实支持每个基本Map都具有的所有功能。所以是的:你可以像任何其他地图一样使用它并忽略所有额外的功能。但那时你基本上会有一个较慢的HashMap。

同步Map和并发Map之间的主要区别在于 - 正如名称所示 - 并发。想象一下,如果您synchronize阻止了99个线程,并且1个可以完成工作,那么您有100个想要从地图中读取的线程。如果您使用并发100个线程可以同时

现在,如果您考虑使用线程的实际原因,您很快就会得出结论:您应该摆脱所有可能的synchronized块。

答案 4 :(得分:1)

这完全取决于你的意思"其他复合操作"通过"工作"。同步与ConcurrentHashMap的工作方式与其他任何对象的工作方式完全相同。

因此,如果您希望将某些复杂的共享状态更改视为原子更改,则必须在同一锁上同步对此共享状态的所有访问。这个锁可以是Map本身,也可以是另一个对象。

答案 5 :(得分:-1)

关于java.util.concurrent.ConcurrentHashMap

  • “在依赖于线程安全而不依赖于其同步细节的程序中与Hashtable完全可互操作:它们不会抛出ConcurrentModificationException。”

  • “允许更新操作”

  • 之间的并发

关于java内存

一般来说,从同步的角度来看,读取是安全的,但不是内存的立场。

另请参阅“http://www.ibm.com/developerworks/java/library/j-jtp03304/”。

因此synchronizatonvolatile应该用于管理并发阅读(与写作相比)。

关于putIfAbsent

putIfAbsent是您的friend

  

如果指定的键尚未与值关联,则关联   它与给定的

value. This is equivalent to
   if (!map.containsKey(key))
       return map.put(key, value);
   else
       return map.get(key);
     

除了动作!!!原子地执行!!!。