如果我有两个多线程访问 HashMap,但保证它们永远不会同时访问同一个键,这是否仍然会导致竞争条件?
原文由 agentofuser 发布,翻译遵循 CC BY-SA 4.0 许可协议
如果我有两个多线程访问 HashMap,但保证它们永远不会同时访问同一个键,这是否仍然会导致竞争条件?
原文由 agentofuser 发布,翻译遵循 CC BY-SA 4.0 许可协议
只需使用 ConcurrentHashMap。 ConcurrentHashMap 使用覆盖一系列哈希桶的多个锁来减少锁被争用的机会。获取无竞争锁对性能的影响很小。
回答您原来的问题:根据 javadoc,只要地图的结构没有改变,您就可以了。这意味着根本不会删除元素,也不会添加地图中不存在的新键。替换与现有键关联的值就可以了。
如果多个线程同时访问一个散列映射,并且至少有一个线程在结构上修改了该映射,则它必须在外部进行同步。 (结构修改是添加或删除一个或多个映射的任何操作;仅更改与实例已包含的键关联的值不是结构修改。)
尽管它不保证可见性。所以你必须愿意接受偶尔检索陈旧的联想。
原文由 Tim Bender 发布,翻译遵循 CC BY-SA 2.5 许可协议
15 回答8.4k 阅读
8 回答6.2k 阅读
1 回答4k 阅读✓ 已解决
3 回答6k 阅读
3 回答2.2k 阅读✓ 已解决
2 回答3.1k 阅读
2 回答3.8k 阅读
在@dotsid 的回答中,他这样说:
他是对的。 A
HashMap
即使 线程使用不相交的密钥集,在没有同步的情况下更新也会中断。这里 只是一些 可能出错的地方。如果一个线程执行
put
,那么另一个线程可能会看到 hashmap 大小的陈旧值。如果一个线程执行
put
并使用(当前)与第二个线程的密钥位于同一哈希桶中的密钥,则第二个线程的映射条目可能会暂时或永久丢失。这取决于哈希链(或其他)的实现方式。当一个线程执行触发表重建的
put
时,另一个线程可能会看到哈希表数组引用、其大小、内容或哈希链的临时或陈旧版本。混乱可能随之而来。当一个线程执行
put
用于与其他线程使用的某个密钥冲突的密钥,而后一个线程为其密钥执行put
时,后者可能会看到陈旧哈希链参考的副本。混乱可能随之而来。当一个线程使用与某个其他线程的键之一冲突的键探测表时,它可能会在链上遇到该键。它将对该键调用 equals,如果线程未同步,equals 方法可能会在该键中遇到陈旧状态。
如果您有两个线程同时执行
put
或remove
请求,则有很多机会出现竞争条件。我可以想到三种解决方案:
ConcurrentHashMap
。HashMap
但在外部同步;例如,使用原始互斥锁、Lock
对象等。但请注意,这可能会由于锁争用而导致并发瓶颈。HashMap
。如果线程真的有一组不相交的键,那么(从算法的角度来看)它们就没有必要共享一个 Map。事实上,如果您的算法涉及线程在某个点迭代映射的键、值或条目,则将单个映射拆分为多个映射可以显着加快该部分的处理速度。1 - 我们无法列举所有可能出错的事情。首先,我们无法预测所有 JVM 将如何处理 JMM 的 未指定 方面……在所有平台上。但无论如何你都不应该依赖那种信息。您需要知道的是,像这样使用
HashMap
是根本错误的。执行此操作的应用程序已损坏……即使您还没有观察到损坏的症状。