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

时间:2014-03-25 11:01:46

标签: java multithreading thread-safety hashset

情况:

多个线程只向非线程安全java.util.HashSet添加值,并且在这些线程停止之前,Set上没有其他操作。

问题:

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

3 个答案:

答案 0 :(得分:4)

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

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

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

但是如果问题是“在访问具有多个线程的not-threadsafe 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将是错误的。当第二次添加条目时,它可以使用(然后) bucketIndex,因此,之后将在地图中包含两次

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

答案 1 :(得分:2)

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

有关此案例的详情,请参阅Paul Tyma article

答案 2 :(得分:2)

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