在多线程环境中,hashmap可以有重复的键

时间:2017-02-21 15:40:58

标签: java hash collections hashmap

如果我们不使用Collections.synchronizedMap()并假设我有一个多线程环境

我知道竞争条件,重新调整等问题。

我的问题是,是否存在2个线程TaTb具有相同对象并尝试放入地图的情况。

是否可以有2个条目,如果没有,它是如何被阻止的。在2个不同线程的2个put调用之间是否有一小部分时间差异。

根据我的理解,对于TaTb,两者都会在放置之前进行检查,因此这里会出现重复键的情况。

考虑到我们已正确覆盖hashcodeequals

1 个答案:

答案 0 :(得分:3)

HashMap州的Javadoc:

  

请注意,此实现未同步。如果有多个线程   同时访问哈希映射,以及至少一个线程   从结构上修改地图,必须在外部同步。 (一个   结构修改是添加或删除一个或多个的任何操作   更多映射;只是改变与键相关的值   实例已经包含的不是结构修改。)这是   通常通过自然地同步某个对象来完成   封装地图。如果不存在这样的对象,则应该是地图   "包裹"使用Collections.synchronizedMap方法。

所以文档说你必须以某种方式同步访问权限,但是不要说如果不这样做会发生什么。这意味着执行此操作时的行为是 undefined - 所有投注均已关闭。

您可以自己查看the source code for HashMapput的核心是:

     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

,仍可能发生这种情况

总之,不要这样做。