Java ConcurrentHashMap和复合操作
Hashtable和Collections.synchronizedMap是线程安全的,但仍然是复合操作,如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
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。”
- “允许更新操作之间并发”
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);