可以在多线程环境中使用hashmap具有重复的键



如果我们不使用 Collections.synchronizedMap(),请说我有多线程环境

我知道种族条件,重新尺寸问题等。

我的问题是可以有一个情况2线程TaTb具有相同对象并尝试放入地图中。

如果没有2个条目,可以预防它。在2个不同线程同时运行的2个不同线程的2个put呼叫之间是否有一小部分的时间差异。

根据我的理解,对于TaTb,都会在放置前检查,因此这里有重复键的情况。

考虑到我们已正确覆盖hashcodeequals

HashMap状态的javadoc:

请注意,此实现不同步。如果多个线程 同时访问哈希地图,至少一个线程 在结构上修改映射,它必须在外部同步。(A 结构修改是任何添加或删除一个或删除一个或 更多映射;只是更改与密钥相关的值 实例已经包含的不是结构性修改。)这是 通常是通过同步的某些物体自然而实现的 封装地图。如果没有这样的对象,则该地图应该是 使用collections.synchronizedmap方法"包装"。

因此,文档说您必须以某种方式同步访问,但不要说如果不这样做会发生什么。这意味着当您这样做时的行为是 undefined - 所有赌注都关闭了。

您可以自己查看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中的实现。Java8的实现很大不同 - 加强了点)

如果两个线程同时尝试此结果,我们可以推测结果 - 但是很难推理。有时会导致两个带有相同键的条目,有时不会。这取决于时间。

TreeMapput()当然是完全不同的,当以这种方式滥用时,它的怪癖会有所不同。

任何这样的行为都是实施的怪癖,并且实现可能会在未来而没有警告的情况下改变,因为我们正在谈论未定义的行为。实施不承诺不会:

  • 默默丢下条目
  • 进入无限循环
  • NullPointerException
  • 要求大量内存
  • 破坏商店,以便丢失其他钥匙的条目
  • 让先前删除的条目重新出现
  • 创建包含堆内存垃圾的条目
  • 等。

docs do 指出,从其他地方进行修改,而 Iterator在对象上工作,将导致 Iterator抛出 ConcurrentModificationException-但这与同步是不同的关注点,并且可以如果您使用SynchronizedMap

,仍然会发生

总而言之,不要这样做。

最新更新