【问题标题】:Concurrent add on non threadsafe HashSet - what is the worst that could happen?并发添加非线程安全的 HashSet - 可能发生的最坏情况是什么?
【发布时间】:2014-05-03 04:13:43
【问题描述】:

情况:

多线程只是将值添加到非线程安全的java.util.HashSet,并且在这些线程停止之前不会对Set 执行其他操作。

问题:

可能发生的最坏情况是什么?

【问题讨论】:

  • “可能发生的最坏情况是什么?” - 太阳的爆炸。
  • 并发问题可能导致世界上任何问题。太阳的爆炸不算什么..

标签: java multithreading thread-safety hashset


【解决方案1】:

这取决于您认为“最差”的情况。

我不确定这个问题是否旨在对当前实现进行详细的技术分析,考虑所有可能的竞争条件和 Java 内存模型的基本细节。

如果问题是:“在当前的实现中可证明会发生什么?”然后我不得不说:“我不知道”。我认为几乎没有人确切地知道这一点。 (这有点像问“以 100 英里/小时的速度撞墙后,汽车的哪些部分会损坏?”——好吧,也许方向盘仍然完好无损,但这有关系吗?)

但是,如果问题是“使用多个线程访问非线程安全的HashMap 时不太可能发生什么?”那么有很多可能的答案:

  • 死锁
  • 例外情况
  • 缺少元素
  • 元素被多次插入
  • 元素被插入错误的哈希箱
  • ...

(根据我对“坏”的主观解释大致排序...)


编辑:对评论的澄清:当然,如果多次调用插入元素,则只能添加两次。根据规范,HashMap 应该最多包含每个键一次。但是向HashMap 添加新条目的调用最终会委托给调用

void createEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<>(hash, key, value, e);
    size++;
}

并且没有(明显的)原因为什么没有其他线程应该在该方法的第一行和第二行之间引起重新哈希(因此,创建一个新的table 数组)。那么这个电话的bucketIndex 是错误的。当第二次添加该条目时,它可以使用(当时)right bucketIndex,因此之后会在地图中两次包含。 p>

但同样:为了真正证明这可能发生,人们必须详细研究实现,这几乎是不可行的。底线是:基本上 anything 在将具有多个线程的元素添加到非线程安全 HashMap 时可能会出错。

【讨论】:

  • 一个元素可能被多次插入或使用错误的哈希?你知道怎么做吗?
  • @mklemenz 添加了编辑(和免责声明)
  • @Marco13,您能解释一下“缺少元素”的情况吗?
  • @IgweKalu 同样,这很难证明,但场景可能类似于“编辑”中描述的场景:现有元素可能会被新元素覆盖(即使新元素有不同的哈希码,而不是equals 现有的),当两个createEntry 调用干扰时。
【解决方案2】:

可能发生的最坏情况(当然除了错误状态)可能是在添加值时出现无限循环,从而阻塞您的线程之一。

有关此案例的更多信息,请参阅Paul Tyma article

【讨论】:

  • 嗯,我今天学到了一些新东西,感谢 Pauls 帖子的链接
【解决方案3】:

我所看到的情况是,您可以在底层 HashMap(用于处理冲突)中获得一个损坏的链表,该链表指向自身。这是我多年来多次看到的问题,它导致线程进入无限循环。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-12
    • 1970-01-01
    • 2010-12-10
    相关资源
    最近更新 更多