HashMap 对于不同的键是线程安全的吗?

新手上路,请多包涵

如果我有两个多线程访问 HashMap,但保证它们永远不会同时访问同一个键,这是否仍然会导致竞争条件?

原文由 agentofuser 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 633
2 个回答

在@dotsid 的回答中,他这样说:

如果您以任何方式更改 HashMap ,那么您的代码就会被破坏。

他是对的。 A HashMap 即使 线程使用不相交的密钥集,在没有同步的情况下更新也会中断。这里 只是一些 可能出错的地方。

  • 如果一个线程执行 put ,那么另一个线程可能会看到 hashmap 大小的陈旧值。

  • 如果一个线程执行 put 并使用(当前)与第二个线程的密钥位于同一哈希桶中的密钥,则第二个线程的映射条目可能会暂时或永久丢失。这取决于哈希链(或其他)的实现方式。

  • 当一个线程执行触发表重建的 put 时,另一个线程可能会看到哈希表数组引用、其大小、内容或哈希链的临时或陈旧版本。混乱可能随之而来。

  • 当一个线程执行 put 用于与其他线程使用的某个密钥冲突的密钥,而后一个线程为其密钥执行 put 时,后者可能会看到陈旧哈希链参考的副本。混乱可能随之而来。

  • 当一个线程使用与某个其他线程的键之一冲突的键探测表时,它可能会在链上遇到该键。它将对该键调用 equals,如果线程未同步,equals 方法可能会在该键中遇到陈旧状态。

如果您有两个线程同时执行 putremove 请求,则有很多机会出现竞争条件。

我可以想到三种解决方案:

  1. 使用 ConcurrentHashMap
  2. 使用常规 HashMap 但在外部同步;例如,使用原始互斥锁、 Lock 对象等。但请注意,这可能会由于锁争用而导致并发瓶颈。
  3. 为每个线程使用不同的 HashMap 。如果线程真的有一组不相交的键,那么(从算法的角度来看)它们就没有必要共享一个 Map。事实上,如果您的算法涉及线程在某个点迭代映射的键、值或条目,则将单个映射拆分为多个映射可以显着加快该部分的处理速度。

1 - 我们无法列举所有可能出错的事情。首先,我们无法预测所有 JVM 将如何处理 JMM 的 未指定 方面……在所有平台上。但无论如何你都不应该依赖那种信息。您需要知道的是,像这样使用 HashMap 是根本错误的。执行此操作的应用程序已损坏……即使您还没有观察到损坏的症状。

原文由 Stephen C 发布,翻译遵循 CC BY-SA 4.0 许可协议

只需使用 ConcurrentHashMap。 ConcurrentHashMap 使用覆盖一系列哈希桶的多个锁来减少锁被争用的机会。获取无竞争锁对性能的影响很小。

回答您原来的问题:根据 javadoc,只要地图的结构没有改变,您就可以了。这意味着根本不会删除元素,也不会添加地图中不存在的新键。替换与现有键关联的值就可以了。

如果多个线程同时访问一个散列映射,并且至少有一个线程在结构上修改了该映射,则它必须在外部进行同步。 (结构修改是添加或删除一个或多个映射的任何操作;仅更改与实例已包含的键关联的值不是结构修改。)

尽管它不保证可见性。所以你必须愿意接受偶尔检索陈旧的联想。

原文由 Tim Bender 发布,翻译遵循 CC BY-SA 2.5 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题