并发添加非线程安全的HashSet – 可能发生的最坏情况是什么?

情况:

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

题:

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

这取决于你认为是“最差”的。

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

因此,如果问题是:“在当前的实施中可以certificate什么?” 然后我不得不说:“我不知道”。 而且我认为几乎没有人知道这一点。 (这有点像问“你以100英里/小时的速度击中一堵墙后,你的汽车的哪些部分会被打破?” – 好吧,也许方向盘仍然完好无损,但这有关系吗?)

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

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

(粗略地按我对“坏”的主观解释排序……)


编辑:评论的澄清:当然,如果插入它的调用多次发生,则只能添加两次元素。 根据具体情况, HashMap最多应包含一次密钥。 但是,向HashMap添加新条目的调用最终会委托给调用

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

并且没有(明显的)理由为什么没有其他线程应该在此方法的第一行和第二行之间导致重新散列(因此,创建新的table数组)。 然后这个调用的bucketIndex将是错误的。 当第二次添加条目时,它可以使用(当时) 右侧 bucketIndex ,因此,之后将在地图中包含两次

但同样:为了真正certificate这可能发生,人们将不得不在一个难以实现的细节中研究实施。 底线是:基本上,当将具有多个线程的元素添加到非线程安全的HashMap时, 任何事情都可能出错。

可能发生的最坏情况(除了错误的状态)当添加一个值时,可能是一个无限循环,阻塞你的一个线程。

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

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