Java ConcurrentHashMap和复合操作

Java ConcurrentHashMap和复合操作,java,collections,thread-safety,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); } } 假设我们使用ConcurrentHas

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的方式与处理任何其他对象的方式完全相同


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

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

您可以始终使用
synchronized
块。
java.util.concurrent
中的奇特集合并不禁止它,它们只是使它对于大多数常见用例来说是多余的。如果您正在执行复合操作(例如,您希望插入两个必须始终具有相同值的键),不仅可以使用外部同步,而且必须使用外部同步

例如:


由于ConcurrentHashMap实现了Map接口,它确实支持每个基本Map都支持的所有功能。所以是的:你可以像其他地图一样使用它,忽略所有额外的功能。但实际上,您将有一个较慢的HashMap

同步映射和并发映射之间的主要区别是(顾名思义)并发性。假设你有100个线程想要从地图中读取,如果你
同步
你屏蔽了99个线程,1个线程可以完成这项工作。如果使用并发,100个线程可以同时工作


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

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

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

使用外部同步的方法可以使用
哈希表
集合。synchronizedMap()
,这仅仅是因为它们保证它们的基本操作也在这些对象上同步。由于
ConcurrentHashMap
不提供这样的保证,原始操作可能会干扰复合操作的执行,从而破坏它们的原子性

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

  • putIfAbsent(键、值)
  • 删除(键、值)
  • 替换(键、值)
  • 替换(键、旧值、新值)
您可以使用这些操作来实现某些复合操作,而无需显式同步,就像您对
原子引用
等所做的那样。

关于
java.util.concurrent.ConcurrentHashMap
  • “在依赖其线程安全性但不依赖其同步细节的程序中,可与哈希表完全互操作:它们不会引发ConcurrentModificationException。”

  • “允许更新操作之间并发”

关于java内存 一般来说,从同步的角度来看,读取是安全的,但从内存的角度来看,读取是不安全的

另见“”

因此,应使用
synchronization
volatile
来管理并发读取(相对于写入)

关于putIfAbsent
putIfAbsent
是您的:

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

value. This is equivalent to
   if (!map.containsKey(key))
       return map.put(key, value);
   else
       return map.get(key);
除了执行操作!!!原子的


您的答案与其他答案相矛盾。但对于其他无法替代的复合操作,如putIfAbsent()method.Updated,该怎么办呢。您必须在
ConcurrentHashMap
上同步所有操作才能使其正常工作。请注意另一个线程在映射上尝试一个基本的冲突操作的情况。@aLearner简短版本:如果您正在与ConcurrentHashMap同步,则说明您做得不对。该类的全部要点是,使用它不需要同步;您可以使用一组不同的操作来避免这种成本。axtavt的答案与您的答案相矛盾。我想听听你们对axtavt给出的答案的看法。
value. This is equivalent to
   if (!map.containsKey(key))
       return map.put(key, value);
   else
       return map.get(key);