如果我有两个多个线程访问一个 HashMap,但保证它们永远不会同时访问同一个键,那还会导致竞争条件吗?
最佳答案
在@dotsid 的回答中,他这样说:
If you change a
HashMap
in any way then your code is simply broken.
他是对的。如果线程使用不相交的键集,则在没有同步的情况下更新的 HashMap
将破坏 even。以下是只是一些1可能出错的地方。
如果一个线程执行
put
,那么另一个线程可能会看到 HashMap 大小的陈旧值。如果一个线程使用与第二个线程的键(当前)在同一个哈希桶中的键执行
put
,则第二个线程的映射条目可能会暂时或永久丢失。这取决于哈希链(或其他)是如何实现的。当一个线程执行
put
触发表的重建时,另一个线程可能会看到哈希表数组引用、其大小、其内容或哈希链的临时或陈旧版本.可能会出现困惑。当一个线程执行
put
的键与其他线程使用的某个键发生冲突,而后一个线程对其键执行put
,那么后者可能会看到哈希链引用的陈旧副本。可能会出现困惑。当一个线程使用与其他线程的键之一冲突的键来探测表时,它可能会在链上遇到该键。它将在该键上调用 equals,如果线程未同步,则 equals 方法可能会在该键中遇到陈旧状态。
如果您有两个线程同时执行 put
或 remove
请求,则会出现许多竞争条件。
我能想到三个解决方案:
- 使用
ConcurrentHashMap
。 - 使用常规的
HashMap
但在外部同步;例如使用原始互斥锁、Lock
对象等。但请注意,这可能会因锁争用而导致并发瓶颈。 - 为每个线程使用不同的
HashMap
。如果线程确实有一组不相交的键,那么(从算法的角度来看)它们应该不需要共享一个 Map。实际上,如果您的算法涉及线程在某个时刻迭代映射的键、值或条目,则将单个映射拆分为多个映射可以显着加快该部分的处理速度。
1 - 我们无法列举所有可能出错的事情。首先,我们无法预测所有 JVM 将如何在所有平台上处理 JMM 的 unspecified 方面。但是无论如何,您都不应该依赖此类信息。你需要知道的是,像这样使用 HashMap
是根本错误的。执行此操作的应用程序已损坏......即使您尚未观察到损坏的症状。
关于java - HashMap 对于不同的键是线程安全的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2688629/