如果我们不使用Collections.synchronizedMap()
并假设我有一个多线程环境。
我知道竞争条件,重新调整等问题。
我的问题是,是否存在2个线程Ta
和Tb
具有相同对象并尝试放入地图的情况。
是否可以有2个条目,如果没有,它是如何被阻止的。在2个不同线程的2个put调用之间是否有一小部分时间差异。
根据我的理解,对于Ta
和Tb
,两者都会在放置之前进行检查,因此这里会出现重复键的情况。
考虑到我们已正确覆盖hashcode
和equals
。
答案 0 :(得分:3)
HashMap
州的Javadoc:
请注意,此实现未同步。如果有多个线程 同时访问哈希映射,以及至少一个线程 从结构上修改地图,必须在外部同步。 (一个 结构修改是添加或删除一个或多个的任何操作 更多映射;只是改变与键相关的值 实例已经包含的不是结构修改。)这是 通常通过自然地同步某个对象来完成 封装地图。如果不存在这样的对象,则应该是地图 "包裹"使用Collections.synchronizedMap方法。
所以文档说你必须以某种方式同步访问权限,但是不要说如果不这样做会发生什么。这意味着执行此操作时的行为是 undefined - 所有投注均已关闭。
您可以自己查看the source code for HashMap
。 put
的核心是:
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
(编辑 - 这是Java 6中的实现.Java 8&s's显着不同 - 这强调了这一点)
如果两个线程同时尝试这一点,我们可以推测结果 - 但很难推理。有时它会导致两个条目具有相同的键,有时它会赢。这取决于时间。
TreeMap
&#39; put()
当然完全不同,以这种方式滥用时的怪癖会有所不同。
任何此类行为都是实现的怪癖,并且实现可能在未来发生变化而不会发出警告,因为我们正在讨论未定义的行为。该实现不会向您承诺它赢得了
:NullPointerException
文档执行声明来自其他地方的修改,而Iterator
正在处理该对象,将导致Iterator
抛出ConcurrentModificationException
- - 但这与同步不同,如果使用SynchronizedMap
总之,不要这样做。