JVM告诉我发生了死锁:
Found one Java-level deadlock:
=============================
"TP-Processor107":
waiting for ownable synchronizer 0x00002aaaf58e70f0, (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync),
which is held by "indexTrackerThread3"
"indexTrackerThread3":
waiting for ownable synchronizer 0x00002aaaf4394580, (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync),
which is held by "TP-Processor16"
"TP-Processor16":
waiting for ownable synchronizer 0x00002aaaf58e70f0, (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync),
which is held by "indexTrackerThread3"
我们可以看到indexTrackerThread3
正在等待TP-Processor16
持有的资源,反之亦然。这确实是一个僵局。
我们可以看到indexTrackerThread3
正在等待0x00002aaaf4394580
:
"indexTrackerThread3":
- parking to wait for <0x00002aaaf4394580>
我的问题:
在线程转储中,为什么没有行- locked <0x00002aaaf4394580>
?
似乎0x00002aaaf58e70f0实际上没有被任何线程锁定。什么可以锁定它?
在我读过的所有死锁文档(示例)中,对于每个不同的- parking to wait for <0x123>
行,总有一个- locked <0x123>
行。所以我开始怀疑JVM错误。我是不是误会了什么?
注意:很抱歉链接到 pastebin,但如果没有完整的转储,这个问题是无法回答的。为简洁起见,我删除了所有包含" at"的行,它们不包含任何锁定信息。
java.util.concurrent 包使用语言外的原生停放机制(以及其他原生机制,如原子比较和交换)。你可以看到我在这里说什么。
您描述的通常在线程转储中出现的模式源于经典的 Java 习语synchronized(lock) { lock.wait(); }
。
Marko Topolnik的回答是正确的。
至于您的问题的解决方案,JProfiler 将向您展示一个完整的线程和监视器图,用于涉及来自 java.util.concurrent 包的锁定情况。
免责声明:我公司开发JProfiler
不同的东西可能会使java线程死锁,监视器,又名同步关键字,只是一件事。
锁可以是内置的对象监视器,一个可拥有的同步器,或与同步器。
您还可以深入了解 ThreadMXBean.findDeadlockedThreads和 ThreadMXBean.findMonitorDeadlockedThreads 的定义以获取更多信息。
就我而言,它是监视器锁定和java 5锁定。
在线程转储中,waiting to lock <0x123>
和locked <0x123>
组合仅用于监视器锁定。使用 java 5 锁定,您只能获得第一部分。像parking to wait for <0x456>
.然后,您在线程转储中搜索一些0x456,但找不到它。这是令人困惑的。
对于这种类型的死锁,线程转储分析的复杂性主要是由于使用了java.util.concurrent 包。这是为了摆脱同步 Java 对象的经典和侵入性方式而构建的。例如,当您希望将 WRITE 操作限制为单个线程模型,同时允许并发读取操作时,此包非常有用。从性能调优的角度来看,这种方法很棒,副作用是在处理并发问题时线程转储分析过程的复杂性增加。
我建议您也查看以下文章。它描述了诸如隐藏的Java死锁场景之类的问题,其中JVM甚至无法检测到死锁(由于READ锁通常不设计为具有所有权概念)。示例 Java 程序作为示例提供。
堆栈跟踪显示0x00002aaaf4394580未被任何线程锁定。这可能是由于 Java 错误 #6822370 而发生的。这一观察应该为投票的答案增加结束。