我正在使用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
它可能卡在别人身上,此外,比较器自定义排序数据方法尤其是它们相同(因为Set不支持相同的元素)而且所有Task
都是新实例,它不应该与其他实例冲突。 / p>
jstack
cmd,主线程卡住了
at java.util.concurrent.ConcurrentSkipListMap.findPredecessor(ConcurrentSkipListMap.java:685)
这是一个错误还是我误导了一些原则?
感谢任何帮助或建议。
更新
我现在尝试修改if (compare == 0) 1 else compare
到if (compare == 0) -1 else compare
,令人惊讶的是,我运作良好!
有没有人可以澄清它是如何工作的?源代码对我来说很难(我想很多人都同意我的观点),毕竟,jdk为机器执行速度做了很多工作而不是代码阅读器。
最后
为了避免尴尬的情况,只需要对comparator
的{{1}}语义进行一些不同的因素,例如附加一个随机值。但我认为它会更好地找到另一个真正合适的集合。
几天前,我发现在systemTime等于时使用hashCode作为第二次验证是个好主意。希望有帮助〜
答案 0 :(得分:1)
您的比较器不稳定。当不同的调用给出不一致的结果时,我不认为ConcurrentSkipListMap
承诺表现得很好。例如,根据您的呼叫方式,您可以同时考虑&lt; b和b&lt; a与您的代码同时进行。