简体   繁体   English

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

[英]Is a HashMap thread-safe for different keys?

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

In @dotsid's answer he says this:在@dotsid 的回答中,他是这样说的:

If you change a HashMap in any way then your code is simply broken.如果您以任何方式更改HashMap ,那么您的代码就会被破坏。

He is correct.他是对的。 A HashMap that is updated without synchronization will break even if the threads are using disjoint sets of keys.即使线程使用不相交的键集,在没有同步的情况下更新的HashMap也会中断。 Here are just some 1 of the things that can go wrong.下面介绍的可能出错的事情了一些1。

  • If one thread does a put , then another thread may see a stale value for the hashmap's size.如果一个线程执行put ,那么另一个线程可能会看到哈希图大小的陈旧值。

  • When a thread does a put that triggers a rebuild of the table, another thread may see transient or stale versions of the hashtable array reference, its size, its contents or the hash chains.当一个线程执行触发表重建的put ,另一个线程可能会看到哈希表数组引用的临时或陈旧版本、其大小、其内容或哈希链。 Chaos may ensue.可能会出现混乱。

  • When a thread does a put for a key that collides with some key used by some other thread, and the latter thread does a put for its key, then the latter might see a stale copy of hash chain reference.当一个线程执行一个put的关键在于与其他某些线程使用的一些关键,而后者螺纹碰撞做了put的关键,那么后者可能会看到的哈希链引用过时副本。 Chaos may ensue.可能会出现混乱。

  • When one thread probes the table with a key that collides with one of some other thread's keys, it may encounter that key on the chain.当一个线程使用与其他线程的键之一冲突的键来探测表时,它可能会在链上遇到该键。 It will call equals on that key, and if the threads are not synchronized, the equals method may encounter stale state in that key.它将对该键调用 equals,如果线程未同步,则 equals 方法可能会在该键中遇到陈旧状态。

And if you have two threads simultaneously doing put or remove requests, there are numerous opportunities for race conditions.如果您有两个线程同时执行putremove请求,则存在大量竞争条件的机会。

I can think of three solutions:我能想到三个解决方案:

  1. Use a ConcurrentHashMap .使用ConcurrentHashMap
  2. Use a regular HashMap but synchronize on the outside;使用常规的HashMap但在外部同步; eg using primitive mutexes, Lock objects, etcetera.例如,使用原始互斥锁、 Lock对象等。
  3. Use a different HashMap for each thread.为每个线程使用不同的HashMap If the threads really have a disjoint set of keys, then there should be no need (from an algorithmic perspective) for them to share a single Map.如果线程确实有一组不相交的键,那么它们就不需要(从算法的角度来看)共享一个 Map。 Indeed, if your algorithms involve the threads iterating the keys, values or entries of the map at some point, splitting the single map into multiple maps could give a significant speedup for that part of the processing.实际上,如果您的算法涉及在某个时刻迭代映射的键、值或条目的线程,那么将单个映射拆分为多个映射可以显着提高该部分的处理速度。

1 - We cannot enumerate all of the possible things that could go wrong. 1 - 我们无法列举所有可能出错的事情。 For a start, we can't predict how all JVMs will handle the unspecified aspects of the JMM ... on all platforms.首先,我们无法预测所有 JVM 将如何处理 JMM 的未指定方面……在所有平台上。 But you should NOT be relying on that kind of information anyway.但无论如何你都不应该依赖这种信息。 All you need to know is that it is fundamentally wrong to use a HashMap like this.您需要知道的是,像这样使用HashMap从根本上是错误的。 An application that does this is broken ... even if you haven't observed the symptoms ... yet.执行此操作的应用程序已损坏...即使您还没有观察到症状...。

Just use a ConcurrentHashMap.只需使用 ConcurrentHashMap。 The ConcurrentHashMap uses multiple locks which cover a range of hash buckets to reduce the chances of a lock being contested. ConcurrentHashMap 使用多个锁来覆盖一定范围的哈希桶,以减少争用锁的机会。 There is a marginal performance impact to acquiring an uncontested lock.获取无争用锁对性能的影响很小。

To answer your original question: According to the javadoc, as long as the structure of the map doesn't change, your are fine.回答您的原始问题:根据 javadoc,只要地图的结构不变,您就可以。 This mean no removing elements at all and no adding new keys that are not already in the map.这意味着根本不会删除元素,也不会添加地图中尚未存在的新键。 Replacing the value associated with existing keys is fine.替换与现有键关联的值很好。

If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally.如果多个线程并发访问一个散列映射,并且至少有一个线程在结构上修改了映射,则必须在外部进行同步。 (A structural modification is any operation that adds or deletes one or more mappings; merely changing the value associated with a key that an instance already contains is not a structural modification.) (结构修改是添加或删除一个或多个映射的任何操作;仅更改与实例已包含的键关联的值不是结构修改。)

Though it makes no guarantees about visibility.虽然它不能保证可见性。 So you have to be willing to accept retrieving stale associations occasionally.因此,您必须愿意接受偶尔检索陈旧的关联。

It depends on what you mean under "accessing".这取决于您在“访问”下的意思。 If you just reading, you can read even the same keys as long as visibility of data guarantied under " happens-before " rules.如果你只是阅读,你甚至可以阅读相同的键,只要在“ happens-before ”规则下保证数据的可见性。 This means that HashMap shouldn't change and all changes (initial constructions) should be completed before any reader start to accessing HashMap .这意味着HashMap不应更改,并且所有更改(初始构造)都应在任何读者开始访问HashMap之前完成。

If you change a HashMap in any way then your code is simply broken.如果您以任何方式更改HashMap ,那么您的代码就会被破坏。 @Stephen C provides very good explanation why. @Stephen C 提供了很好的解释。

EDIT: If the first case is your actual situation, I recommend you to use Collections.unmodifiableMap() to be shure that your HashMap is never changed.编辑:如果第一种情况是您的实际情况,我建议您使用Collections.unmodifiableMap()确保您的 HashMap 永远不会改变。 Objects which are pointed by HashMap should not change also, so aggressive using final keyword can help you. HashMap指向的对象也不应该改变,所以积极使用final关键字可以帮助你。

And as @Lars Andren says, ConcurrentHashMap is best choice in most cases.正如@Lars Andren 所说, ConcurrentHashMap在大多数情况下是最佳选择。

Modifying a HashMap without proper synchronization from two threads may easily lead to a race condition.在没有从两个线程正确同步的情况下修改 HashMap 可能很容易导致竞争条件。

  • When a put() leads to a resize of the internal table, this takes some time and the other thread continues to write to the old table.put()导致内部表的大小调整时,这需要一些时间,另一个线程继续写入旧表。
  • Two put() for different keys lead to an update of the same bucket if the keys' hashcodes are equal modulo the table size.如果键的哈希码等于表大小的模数,则针对不同键的两个put()会导致更新同一存储桶。 (Actually, the relation between hashcode and bucket index is more complicated, but collisions may still occur.) (其实hashcode和bucket index的关系比较复杂,但是还是有可能会发生冲突。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM