【问题标题】:can hashmap have duplicate keys in multithreading environment [duplicate]hashmap 在多线程环境中可以有重复的键吗[重复]
【发布时间】:2017-07-11 08:04:06
【问题描述】:

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

我知道竞态条件、调整大小等问题。

我的问题是,是否有一个案例 2 线程 TaTb 具有相同的对象并试图放入地图中。

是否可以有 2 个条目,如果不是如何防止的话。同时运行的 2 个不同线程的 2 个 put 调用之间是否存在时间差异。

据我了解,TaTb 都会在放置前检查,所以这里会不会出现重复键的情况。

考虑到我们已经正确覆盖了hashcodeequals

【问题讨论】:

  • 我认为它们总是会相互覆盖。如果你没有正确同步它,你可能会得到一个并发异常。
  • 你为什么这么问?您肯定不打算在这种情况下使用非线程安全容器吗?
  • 因为在放置之前都会检查,所以你有你的竞争条件:插入不是原子(单个不可分割)操作,但它至少包含 2 个操作当TaTb 未正确同步时可能会产生干扰。
  • 不,HashMap 中永远不会有重复的键,因为它是如何实现的。只会有一个竞争条件。
  • @PatrickParker 有人问我这个问题,我知道我可以使用同步功能,但这就像一种解决方法,我更感兴趣的是真正知道到底发生了什么,就像我可以说的那样, read might happenput call 之前,所以它可能返回 null,但我担心的是同时写入同一个对象

标签: java hash collections hashmap


【解决方案1】:

HashMap 的 Javadoc 声明:

注意这个实现是不同步的。如果多线程 同时访问哈希映射,并且至少访问其中一个线程 在结构上修改地图,它必须在外部同步。 (一种 结构修改是添加或删除一个或 更多映射;仅仅改变与一个键关联的值 实例已经包含不是结构修改。)这是 通常通过同步一些自然而然的对象来完成 封装地图。如果不存在这样的对象,则地图应该是 使用 Collections.synchronizedMap 方法“包装”。

所以文档说您必须以某种方式同步访问,但不要说如果不这样做会发生什么。这意味着当你这样做时的行为是未定义——所有的赌注都被取消了。

您可以自己查看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 有很大的不同 - 这加强了这一点)

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

TreeMapput()当然是完全不一样的,被这样滥用时的怪癖也会不一样。

任何此类行为都是实现的怪癖,并且实现可能会在没有警告的情况下更改,因为我们正在谈论未定义的行为。该实现不向您承诺它不会:

  • 静默删除条目
  • 进入无限循环
  • NullPointerException
  • 申请大量内存
  • 损坏存储,导致其他键的条目丢失
  • 使以前删除的条目重新出现
  • 从堆内存中创建包含垃圾的条目

文档 do 声明,当 Iterator 正在处理对象时,来自其他地方的修改将导致 Iterator 抛出 ConcurrentModificationException - 但这是不同的同步问题,如果您使用SynchronizedMap,仍然可能发生

总而言之,不要这样做。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-23
    • 1970-01-01
    • 2014-10-10
    • 1970-01-01
    • 2017-07-11
    • 1970-01-01
    • 2022-01-20
    • 1970-01-01
    相关资源
    最近更新 更多