java - 为什么从静态初始化程序启动线程并等待其完成会导致死锁?
问题描述
我从那个答案中获取代码 - https://stackoverflow.com/a/9286697/2674303
我创建当前主题的原因是我不明白为什么该代码会导致死锁:
public class Lock implements Runnable {
static {
System.out.println(Thread.currentThread().getId() + "# Getting ready to greet the world");
try {
System.out.println(Thread.currentThread().getId() + "# before lock creation");
Lock target = new Lock();
System.out.println(Thread.currentThread().getId() + "# after lock creation");
Thread t = new Thread(target);
t.start();
System.out.println(Thread.currentThread().getId() + "# awaiting thread finish");
t.join();
System.out.println(Thread.currentThread().getId() + "# static block finished");
} catch (InterruptedException ex) {
System.out.println("won't see me");
}
}
public static void main(String[] args) {
System.out.println(Thread.currentThread() + "Hello World! ");
}
public void run() {
System.out.println(Thread.currentThread().getId() + "# Started thread");
Thread t = new Thread(new Lock());
t.start();
}
}
我尝试启动它很多时间,它总是导致死锁。
输出始终相同:
1# Getting ready to greet the world
1# before lock creation
1# after lock creation
1# awaiting thread finish
13# Started thread
我试图使初始化程序成为非静态的,并且在它之后代码变得不会导致死锁。所以我相信它在某种程度上与静态类初始化有关。
你能解释一下吗?
回答
感谢 John Skeet 的回答,但为了简化事情,我删除了阻止我理解该示例的代码行:
public class Lock implements Runnable {
static {
try {
Thread thread = new Thread(new Lock());
thread.start();
thread.join();
} catch (InterruptedException ex) {
System.out.println("won't see me");
}
}
public static void main(String[] args) {
System.out.println(Thread.currentThread() + "Hello World! ");
}
public void run() {
new Lock();
}
}
也会导致僵局
解决方案
新线程试图run
在Lock
类内调用。该方法本身尝试创建Lock
. 在类完成初始化之前,它不能这样做1 。Lock
JVM 知道另一个线程已经在初始化这个类,所以它会阻塞直到初始化完成。
run
不幸的是,由于t.join()
调用,该初始化在完成之前无法完成。所以两个线程中的每一个都需要另一个线程在它可以做任何事情之前取得进展 - 死锁。
正是出于这个原因,绝对值得尝试避免在类初始化器中做大量工作。
即使run()
方法本身是空的,这一切都会发生。但比这更糟糕 - 因为该run()
方法在创建另一个线程并等待该线程完成调用run()
等之前不会完成。所以这是另一个失败的原因 - 它基本上会产生线程,直到 JVM 用完资源。因此,即使删除类型初始值设定项也不会让您使用工作代码。
关于为什么需要类型初始化,请参阅JLS 的第 12.4.1 节:
类或接口类型 T 将在以下任何一项第一次出现之前立即初始化:
- T 是一个类,并创建了一个 T 的实例。
- 调用由 T 声明的静态方法。
- 分配了一个由 T 声明的静态字段。
- 使用了由 T 声明的静态字段,并且该字段不是常量变量(第 4.12.4 节)。
该表达式new Lock()
将始终初始化Lock
或等待初始化完成(当然,这可能已经发生)。
1如果您拆分代码run
以便创建实例Lock
然后记录,然后启动线程,您会看到这是Lock
阻塞的创建。
推荐阅读
- python - python多处理池(1)
- php - PHP循环一个队列数据,并在第一个可见子DIV处输出当前队列号
- excel - 如何使用 excel 公式计算 yyyymm 格式的两个日期时间值之间的差异?
- authentication - 如何使用 HttpOnly 身份验证会话 cookie 保护 SPA 中的路由?
- c++ - 剪辑问题添加音符以创建和弦
- amazon-web-services - AWS EC2 实例的访问策略
- python - 我们如何比较 django 中的请求对象?
- pytorch - 导出一个 torchscript 模型
- javascript - Passport.js - 自定义未经授权的消息
- java - 如何远程执行测试代码(maven/java/testNG/selenium)?