Java ConcurrentHashMap完全安全吗?
这是一篇来自JavaDoc的关于Java ConcurrentHashMap完全安全吗?,java,multithreading,synchronized,java.util.concurrent,Java,Multithreading,Synchronized,Java.util.concurrent,这是一篇来自JavaDoc的关于ConcurrentHashMap的文章。它说检索操作通常不会阻塞,因此可能与更新操作重叠。这是否意味着get()方法不是线程安全的 但是,即使所有操作都是线程安全的,检索 操作不需要锁定,也不支持 以阻止所有访问的方式锁定整个表。此类 在依赖其 线程安全性,但不取决于其同步细节 检索操作(包括get)通常不会阻塞,因此 与更新操作重叠(包括放置和删除)。检索 反映最近完成的更新操作的结果 在他们开始的时候坚持。” ConcurrentHashmap.get()是
ConcurrentHashMap
的文章。它说检索操作通常不会阻塞,因此可能与更新操作重叠。这是否意味着get()
方法不是线程安全的
但是,即使所有操作都是线程安全的,检索
操作不需要锁定,也不支持
以阻止所有访问的方式锁定整个表。此类
在依赖其
线程安全性,但不取决于其同步细节
检索操作(包括get)通常不会阻塞,因此
与更新操作重叠(包括放置和删除)。检索
反映最近完成的更新操作的结果
在他们开始的时候坚持。”
ConcurrentHashmap.get()
是线程安全的,即
- 它不会引发任何异常,包括
ConcurrentModificationException
- 它将返回一个在过去某个时间(最近)为真的结果。这意味着要获取的两个背靠背调用可以返回不同的结果。当然,这也适用于任何其他
Map
get()
然而,尽管ConcurrentHashMap
是线程安全的替代品,但重要的是要认识到,如果要执行多个操作,可能必须显著更改代码。例如,以以下代码为例:
if (!map.containsKey(key))
return map.put(key, value);
else
return map.get(key);
在多线程环境中,这是一种竞争条件。您必须使用,并注意返回值,它会告诉您put操作是否成功。阅读文档了解更多详细信息
回答一个要求澄清为什么这是一个种族条件的评论
假设有两个线程A
,B
将在映射中放置两个不同的值,v1
和v2
分别具有相同的键。该键最初不在地图中。它们以这种方式交错:
- 线程
A
调用containsKey
并发现密钥不存在,但立即挂起
- 线程
B
调用containsKey
,发现密钥不存在,并有时间插入其值v2
- 线程
A
恢复并插入v1
,“和平地”覆盖(因为put
是线程安全的)线程B
插入的值
现在线程B
“认为”它已经成功地插入了自己的值v2
,但是映射包含v1
。这确实是一场灾难,因为线程B
可能会调用v2.updateMething()
,并且会“认为”映射的使用者(例如其他线程)有权访问该对象,并会看到可能很重要的更新(“例如:该访问者IP地址正在尝试执行DOS,从现在开始拒绝所有请求”)。相反,对象将很快被垃圾收集并丢失 HashMap
根据hashCode
分为两部分ConcurrentHashMap
使用此事实。它的同步机制基于阻塞桶而不是整个Map
。这样,几个线程可以同时写入几个不同的bucket(一个线程一次可以写入一个bucket)
从ConcurrentHashMap
读取几乎不使用同步。同步是在获取键的值时看到null
值时使用的。由于<代码> CONTURNESHASMAP 不能存储<代码> null <代码>作为值(是的,除了关键字之外,值也不能是代码> null < /代码> s),这意味着读取另一个线程时初始化映射项(键值对)时发生的“<代码> null >代码>:当键被分配,但值尚未被分配时,并且它仍然保持默认的空值。
在这种情况下,读取线程将需要等待,直到条目被完全写入
因此,read()
的结果将基于地图的当前状态。如果你读了更新中的关键字的值,那么由于写过程还没有完成,你很有可能得到旧的值。然而,线程安全的方式可能不是您所期望的。您可以从中看到一些“提示”:
此类可在以下程序中与哈希表
完全互操作:
依赖于它的线程安全性,但不依赖于它的同步细节
要更全面地了解整个情况,您需要了解ConcurrentMap
界面
原始的Map
提供了一些非常基本的读取/更新方法。甚至我也能够实现Map
的线程安全实现;在很多情况下,如果不考虑我的同步机制,人们就无法使用我的地图。这是一个典型的例子:
if (!threadSafeMap.containsKey(key)) {
threadSafeMap.put(key, value);
}
这段代码不是thread-s
synchronized(threadSafeMap) {
if (!threadSafeMap.containsKey(key)) {
threadSafeMap.put(key, value);
}
}
// only remove if both key1 and key2 exists
if (map.containsKey(key1) && map.containsKey(key2)) {
map.remove(key1);
map.remove(key2);
}