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