如果我有两个多个线程访问一个HashMap,但是要保证它们永远不会同时访问同一密钥,那还会导致争用情况吗?
Answers:
在@dotsid的回答中,他说:
如果您以任何方式更改HashMap,则代码将被破坏。
他是正确的。即使线程使用的是不相交的键集,在没有同步的情况下更新的HashMap也会中断。这是一些可能出错的事情。
如果一个线程执行put
,则另一线程可能会看到哈希图大小的陈旧值。
当线程执行put
触发表重建的操作时,另一个线程可能会看到哈希表数组引用的瞬时或陈旧版本,其大小,其内容或哈希链。可能会发生混乱。
当线程对put
与其他线程使用的某个密钥冲突的密钥执行a操作,而后者put
对密钥进行操作时,后者可能会看到哈希链引用的陈旧副本。可能会发生混乱。
当一个线程用与其他某个线程的键之一冲突的键探测表时,它可能会在链上遇到该键。它将在该键上调用equals,如果线程未同步,则equals方法可能在该键上遇到陈旧状态。
并且,如果您有两个线程同时执行put
或remove
请求操作,则有很多竞争条件的机会。
我可以想到三种解决方案:
ConcurrentHashMap
。HashMap
但在外部同步;例如使用原始互斥量,Lock
对象等。HashMap
为每个线程使用不同的名称。如果线程确实有一组不相交的键,那么(从算法的角度来看)应该不需要它们共享单个Map。确实,如果您的算法涉及某个线程在某个点迭代映射的键,值或条目,则将单个映射拆分为多个映射可以显着加快该部分处理。只需使用ConcurrentHashMap。ConcurrentHashMap使用覆盖多个哈希桶的多个锁,以减少争用锁的机会。获取无争议的锁会对性能产生边际影响。
回答您的原始问题:根据javadoc,只要地图的结构不变,就可以了。这意味着根本不会删除任何元素,也不会添加地图中尚未存在的新键。替换与现有键关联的值就可以了。
如果多个线程同时访问哈希映射,并且至少有一个线程在结构上修改该映射,则必须在外部进行同步。(结构修改是添加或删除一个或多个映射的任何操作;仅更改与实例已经包含的键相关联的值不是结构修改。)
尽管它不能保证可见性。因此,您必须偶尔愿意接受检索过时的关联。
这取决于您在“访问”下的含义。如果您只是阅读,只要按“事前发生”规则保证了数据的可见性,您甚至可以阅读相同的键。这意味着HashMap
不应更改,所有更改(初始构造)都应在任何读者开始访问之前完成HashMap
。
如果您HashMap
以任何方式进行更改,那么您的代码就会被破坏。@Stephen C提供了很好的解释。
编辑:如果第一种情况是您的实际情况,我建议您使用Collections.unmodifiableMap()
以确保您的HashMap永不更改。指向的对象HashMap
也不应更改,因此积极使用final
关键字可以为您提供帮助。
正如@Lars Andren所说,ConcurrentHashMap
在大多数情况下是最佳选择。
unmodifiableMap
确保客户端无法更改地图。它不会做任何事情来确保基础图不变。