【发布时间】:2014-12-18 15:09:49
【问题描述】:
(在 Java 中使用静态初始化器是否是一个好主意超出了这个问题的范围。)
我在我的 Scala 应用程序中遇到死锁,我认为这是由已编译类中的互锁静态初始化程序引起的。
我的问题是如何检测和诊断这些死锁——我发现当涉及到静态初始化块时,用于死锁的普通 JVM 工具似乎不起作用。
这是一个简单的 Java 应用示例,它在静态初始化程序中死锁:
public class StaticDeadlockExample implements Runnable
{
static
{
Thread thread = new Thread(
new StaticDeadlockExample(),
"StaticDeadlockExample child thread");
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args)
{
System.out.println("in main");
}
public static void sayHello()
{
System.out.println("hello from thread " + Thread.currentThread().getName());
}
@Override
public void run() {
StaticDeadlockExample.sayHello();
}
}
如果您启动此应用程序,它会死锁。死锁时的堆栈跟踪(来自jstack)包含以下两个死锁线程:
"StaticDeadlockExample child thread" prio=6 tid=0x000000006c86a000 nid=0x4f54 in Object.wait() [0x000000006d38f000]
java.lang.Thread.State: RUNNABLE
at StaticDeadlockExample.run(StaticDeadlockExample.java:37)
at java.lang.Thread.run(Thread.java:619)
Locked ownable synchronizers:
- None
"main" prio=6 tid=0x00000000005db000 nid=0x2fbc in Object.wait() [0x000000000254e000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000004a6a7870> (a java.lang.Thread)
at java.lang.Thread.join(Thread.java:1143)
- locked <0x000000004a6a7870> (a java.lang.Thread)
at java.lang.Thread.join(Thread.java:1196)
at StaticDeadlockExample.<clinit>(StaticDeadlockExample.java:17)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:169)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:116)
Locked ownable synchronizers:
- None
我的问题如下
- 为什么第一个线程被标记为 RUNNABLE,而实际上它正在等待锁?我能以某种方式检测到这个线程的“真实”状态吗?
- 为什么两个线程都没有标记为拥有任何(相关)锁,而实际上一个线程持有静态初始化程序锁而另一个正在等待它?我能以某种方式检测静态初始化锁的所有权吗?
【问题讨论】:
-
为什么你认为它正在等待锁?如果它正在等待一个锁,它将 (1) 处于 WAITING 状态,而不是 RUNNABLE 并且 (2) 在堆栈的顶部条目之后会提到“-waiting on ...”或“- waiting to lock”。
-
我认为these initialization locks 不可见。它们由 JVM 专门为此进程管理。也许线程转储没有显示它们。
-
@Dima -- 我知道它正在等待
锁,因为我构建了代码以便它可以。如果你运行上面的程序,它会永远死锁——你自己试试。你的意思是它没有使用 java.util.concurrent.locks吗?如果是这样 - 那是对的,但不是很有帮助。 -
@SotiriosDelimanolis 是的,锁似乎在线程转储中不可见。问题中有一个工作示例证明了这一事实。我的问题是如何让它们可见。
-
这是一个非常有趣的问题,但 IMO 这个例子有点……差。初始化器用于初始化静态状态,但这里的初始化器不仅被滥用于运行业务逻辑,还被滥用于启动一个单独的线程。可怜的,可怜的 JVM。