类初始化死锁不报 BLOCKED,也不会被 ThreadMXBean 发现。这篇把它复现出来,看线程转储里的特征,量它的时间窗口,并看单线程循环初始化与初始化失败的行为。
[A ↔ B]
[ 3 ms][T2] B.<clinit> 开始
[ 3 ms][T1] A.<clinit> 开始
1.5 秒后 T1 状态 RUNNABLE,T2 状态 RUNNABLE(isAlive true/true)
ThreadMXBean.findDeadlockedThreads() = null;findMonitorDeadlockedThreads() = null
"T1" #20 [26371] daemon prio=5 os_prio=31 cpu=7.56ms elapsed=3.09s tid=0x0000007b9b0bea00 nid=26371 in Object.wait()
java.lang.Thread.State: RUNNABLE
at A.<clinit>(A.java:1)
- waiting on the Class initialization monitor for B
at Main$$Lambda/0x0000009001000c10.run(Unknown Source)
at java.lang.Thread.runWith(java.base@21.0.12.1/Thread.java:1596)
at java.lang.Thread.run(java.base@21.0.12.1/Thread.java:1583)
"T2" #21 [26115] daemon prio=5 os_prio=31 cpu=9.33ms elapsed=3.09s tid=0x0000007b9b0bf100 nid=26115 in Object.wait()
java.lang.Thread.State: RUNNABLE
at B.<clinit>(B.java:1)
- waiting on the Class initialization monitor for A
at Main$$Lambda/0x0000009001001a08.run(Unknown Source)
at java.lang.Thread.runWith(java.base@21.0.12.1/Thread.java:1596)
at java.lang.Thread.run(java.base@21.0.12.1/Thread.java:1583)
T1 的栈(Thread.getStackTrace):
A.<clinit>(A.java:1)
java.base/java.lang.Thread.run(Thread.java:1583)
T2 的栈(Thread.getStackTrace):
B.<clinit>(B.java:1)
java.base/java.lang.Thread.run(Thread.java:1583) [Parent ↔ Child]
[ 3 ms][T1] Parent.<clinit> 开始(先停一会儿,再创建 Child)
1.5 秒后 T1 状态 RUNNABLE,T2 状态 RUNNABLE(isAlive true/true)
ThreadMXBean.findDeadlockedThreads() = null;findMonitorDeadlockedThreads() = null
"T1" #20 [27395] daemon prio=5 os_prio=31 cpu=9.73ms elapsed=3.16s tid=0x00000079b70c6a00 nid=27395 in Object.wait()
java.lang.Thread.State: RUNNABLE
at Parent.<clinit>(Parent.java:1)
- waiting on the Class initialization monitor for Child
at Main$$Lambda/0x0000008001000c10.run(Unknown Source)
at java.lang.Thread.runWith(java.base@21.0.12.1/Thread.java:1596)
at java.lang.Thread.run(java.base@21.0.12.1/Thread.java:1583)
"T2" #21 [26883] daemon prio=5 os_prio=31 cpu=0.25ms elapsed=3.10s tid=0x00000079b70c7100 nid=26883 in Object.wait()
java.lang.Thread.State: RUNNABLE
at java.lang.Class.forName0(java.base@21.0.12.1/Native Method)
- waiting on the Class initialization monitor for Parent
at java.lang.Class.forName(java.base@21.0.12.1/Class.java:423)
at java.lang.Class.forName(java.base@21.0.12.1/Class.java:414)
at Main.lambda$main$0(Main.java:27)
at Main$$Lambda/0x0000008001001400.run(Unknown Source)
at java.lang.Thread.runWith(java.base@21.0.12.1/Thread.java:1596)
at java.lang.Thread.run(java.base@21.0.12.1/Thread.java:1583)
T1 的栈(Thread.getStackTrace):
Parent.<clinit>(Parent.java:1)
java.base/java.lang.Thread.run(Thread.java:1583)
T2 的栈(Thread.getStackTrace):
java.base/java.lang.Class.forName0(Native Method)
java.base/java.lang.Class.forName(Class.java:423)
java.base/java.lang.Class.forName(Class.java:414)
Main.lambda$main$0(Main.java:27)
java.base/java.lang.Thread.run(Thread.java:1583) [单线程]
[ 0 ms][main] 单线程:先访问 X
[ 13 ms][main] Y() 构造时 X.s = null
[ 13 ms][main] new X().get() = x-ready [Bad]
[ 0 ms][main] 第 1 次访问 Bad:java.lang.ExceptionInInitializerError ← cause: java.lang.NumberFormatException: For input string: "not a number"
[ 13 ms][main] 第 2 次访问 Bad:java.lang.NoClassDefFoundError: Could not initialize class Bad ← cause: java.lang.ExceptionInInitializerError: Exception java.lang.NumberFormatException: For input strin
[ 13 ms][main] 第 3 次访问 Bad:java.lang.NoClassDefFoundError: Could not initialize class Bad ← cause: java.lang.ExceptionInInitializerError: Exception java.lang.NumberFormatException: For input strin [T2 晚 0 µs]
<clinit> 里等待 0 ms,T2 比 T1 晚 0 µs 启动:40 轮中死锁 40 轮
[T2 晚 200 µs]
<clinit> 里等待 0 ms,T2 比 T1 晚 200 µs 启动:40 轮中死锁 40 轮
[T2 晚 1000 µs]
<clinit> 里等待 0 ms,T2 比 T1 晚 1000 µs 启动:40 轮中死锁 4 轮
[T2 晚 5000 µs]
<clinit> 里等待 0 ms,T2 比 T1 晚 5000 µs 启动:40 轮中死锁 0 轮
[T2 晚 20000 µs]
<clinit> 里等待 0 ms,T2 比 T1 晚 20000 µs 启动:40 轮中死锁 0 轮
(<clinit> 里等待 100 ms 的对照)
[等待 100 ms,晚 5000 µs]
<clinit> 里等待 100 ms,T2 比 T1 晚 5000 µs 启动:40 轮中死锁 40 轮 [Holder]
[ 3 ms][T1] T1 得到 holder-0
[ 3 ms][T2] T2 得到 holder-0
[ 15 ms][main] T1 alive=false T2 alive=false