使用 java ConcurrentSkipListSet 添加方法时线程卡住

Thread stuck when use java ConcurrentSkipListSet add method

我正在使用 ConcurrentSkipListSet 集合来处理并发运算符。我发现它有时会卡住,用这个代码重现:

import java.util.Comparator
import java.util.concurrent._

object SetDeadLock extends App {
  private val tasks = new ConcurrentSkipListSet[Task](new Comparator[Task](){
    override def compare(o1: Task, o2: Task): Int = {
      val compare = (o1.systemTime - o2.systemTime).toInt
      if (compare == 0) 1 else compare  //distinct same time task
    }
  })

  for(i <- 1 to 20) {
    tasks.add(Task())
    println(s"added - $i")
  }

  case class Task() {
    val systemTime = System.currentTimeMillis()
  }
}

输出

added - 1
added - 2

可能卡在别人身上了,另外comparator自定义排序数据的方法特别是相同(因为Set不支持相同的元素)而且Task都是new instance,应该不会和别人冲突.

jstackcmd,主线程卡住了

at java.util.concurrent.ConcurrentSkipListMap.findPredecessor(ConcurrentSkipListMap.java:685)

这是一个错误还是我只是误导了一些原则?

感谢任何帮助或建议。

更新
我刚刚尝试将if (compare == 0) 1 else compare修改为if (compare == 0) -1 else compare,令人惊讶的是,我工作得很好!

有谁能解释一下它是如何工作的吗?源代码对我来说很难(我想很多人都同意我的看法),毕竟,jdk 做很多工作是为了机器执行速度而不是为了代码 reader。

终于
为了避免尴尬的情况,只需为 comparator 添加一些与 Set 语义匹配的不同因素,例如附加一个随机 value.But 我认为最好找到另一个真正合适的集合。​​

几天前,我发现一个好主意,当系统时间等于时,使用 hashCode 作为第二个验证。希望有帮助~

您的比较器不稳定。我不认为 ConcurrentSkipListMap 承诺在不同的调用给出不一致的结果时表现良好。例如,根据您的调用方式,您可以在代码中同时考虑 a < b 和 b < a。