它是线程安全的。然而,它是线程安全的方式可能不是你所期望的。还有一些“线索”,你可以看到:
这个类是完全的互操作性与Hashtable
在 依赖于它的线程安全程序,但不是它的同步信息
要知道整个故事在更完整的图片中,您需要了解接口ConcurrentMap
。
原来Map
提供了一些非常基本的读/更新方法。即使我能够做一个线程安全的执行Map
;没有考虑我的同步机制,人们无法使用我的地图的情况很多。这是一个典型的例子:
if (!threadSafeMap.containsKey(key)) {
threadSafeMap.put(key, value);
}
这段代码不是线程安全的,即使地图本身是。两个线程在同一时间调用containsKey()
可能会认为没有这样的密钥,因此它们都插入到Map
中。
为了解决这个问题,我们需要明确地做额外的同步。假设我的地图的线程安全性是通过同步关键字来实现,你需要做的:
synchronized(threadSafeMap) {
if (!threadSafeMap.containsKey(key)) {
threadSafeMap.put(key, value);
}
}
这种额外的代码需要你了解地图的“同步信息”。在上面的例子中,我们需要知道同步是通过“synchronized”来实现的。
ConcurrentMap
接口借此一步。它定义了一些涉及到多次访问地图的常见“复杂”操作。例如,上面的示例显示为putIfAbsent()
。通过这些“复杂”操作,ConcurrentMap
(大多数情况下)的用户不需要将动作与多次访问同步到地图。因此,Map的实现可以执行更复杂的同步机制以获得更好的性能。 ConcurrentHashhMap
就是一个很好的例子。线程安全实际上是通过为地图的不同分区保留单独的锁来维护的。它是线程安全的,因为到地图中不会损坏内部数据结构,或并发访问造成任何更新丢失意外等
考虑到上述所有,Javadoc中的意义将更加清晰:
“检索操作(包括get)通常不会阻止”,因为ConcurrentHashMap
因为其线程安全性未使用“synchronized”。 get
本身的逻辑处理线程安全性;并且如果您在Javadoc看得更远:
表在内部划分,试图以允许并发更新指定数量的 不争
不仅是检索非阻塞的,甚至更新可以同时发生。但是,非阻塞/并发更新并不意味着它是线程UNsafe。它仅仅意味着它正在使用除简单的“同步”之外的其他方法来实现线程安全。
但是,由于内部同步机制未公开,如果您想执行ConcurrentMap
以外的复杂操作,您可能需要考虑更改逻辑或考虑不使用ConcurrentHashMap
。例如:
// only remove if both key1 and key2 exists
if (map.containsKey(key1) && map.containsKey(key2)) {
map.remove(key1);
map.remove(key2);
}
如果我正在阅读该权限,这意味着检索将始终返回上次更新的结果以在检索开始时完成。这意味着完成更新可以在检索开始和完成之间发生,并且不会改变所述检索的结果。 – Aurand 2013-02-19 00:29:53