对于不同的密钥,哈希映射线程是否安全?

如果我有两个多线程访问HashMap,但保证它们永远不会同时访问相同的密钥,这是否仍然会导致争用情况?


答案 1

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

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

他是对的。在没有同步的情况下更新的 将中断,即使线程使用不相交的键集也是如此。以下是一些可能出错的事情。HashMap

  • 如果一个线程执行 ,则另一个线程可能会看到哈希映射大小的过时值。put

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

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

  • 当线程对某个键执行 a 操作,该键与某个其他线程使用的某个键发生冲突,而后者对其键执行 a 时,后者可能会看到哈希链引用的过时副本。混乱可能会随之而来。putput

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

而且,如果您有两个线程同时执行或请求,则存在许多争用条件的机会。putremove

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

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

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


答案 2

只需使用 ConcurrentHashMap 即可。ConcurrentHashMap使用多个锁,这些锁覆盖了一系列哈希桶,以减少锁被质疑的机会。获得无争议锁具的性能影响很小。

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

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

虽然它不保证可见性。因此,您必须愿意偶尔接受检索过时的关联。