它是线程安全的。但是,线程安全的方式可能不是您所期望的。您可以从中看到一些“提示”:
此类与Hashtable
依赖其线程安全但不依赖其同步细节的程序完全可互操作
要更全面地了解整个故事,您需要了解ConcurrentMap
界面。
原文Map
提供了一些非常基本的读取/更新方法。甚至我也能够对 ; 进行线程安全的实现Map
。在很多情况下,如果不考虑我的同步机制,人们就无法使用我的 Map。这是一个典型的例子:
if (!threadSafeMap.containsKey(key)) {
threadSafeMap.put(key, value);
}
这段代码不是线程安全的,尽管映射本身是。同时调用containsKey()
的两个线程可能认为没有这样的键,因此它们都插入到Map
.
为了解决这个问题,我们需要明确地进行额外的同步。假设我的 Map 的线程安全是通过同步关键字实现的,您需要执行以下操作:
synchronized(threadSafeMap) {
if (!threadSafeMap.containsKey(key)) {
threadSafeMap.put(key, value);
}
}
这样的额外代码需要你了解地图的“同步细节”。在上面的例子中,我们需要知道同步是通过“synchronized”来实现的。
ConcurrentMap
界面更进一步。它定义了一些常见的“复杂”操作,涉及对地图的多次访问。例如,上面的例子被暴露为putIfAbsent()
. 使用这些“复杂”操作,ConcurrentMap
(在大多数情况下)用户不需要同步操作与对地图的多次访问。因此,Map 的实现可以执行更复杂的同步机制以获得更好的性能。 ConcurrentHashhMap
是一个很好的例子。线程安全实际上是通过为映射的不同分区保持单独的锁来维护的。它是线程安全的,因为对 map 的并发访问不会破坏内部数据结构,或者导致任何更新丢失意外等。
考虑到以上所有,Javadoc的含义会更清楚:
“检索操作(包括获取)通常不会阻塞”,因为ConcurrentHashMap
它没有使用“同步”来保证其线程安全。本身的逻辑get
负责线程安全;如果您进一步查看 Javadoc:
该表在内部进行了分区,以尝试允许指定数量的并发更新而不会发生争用
不仅检索是非阻塞的,甚至更新也可以同时发生。但是,非阻塞/并发更新并不意味着它是线程不安全的。它只是意味着它使用了一些不同于简单“同步”的方式来保证线程安全。
ConcurrentMap
但是,由于内部同步机制没有暴露出来,如果你想做一些除了ConcurrentHashMap
. 例如:
// only remove if both key1 and key2 exists
if (map.containsKey(key1) && map.containsKey(key2)) {
map.remove(key1);
map.remove(key2);
}