为什么从静态初始化程序启动线程并等待其完成会导致死锁?
Why starting thread from static initializer and awaiting its finishing leads to deadlock?
我从那个答案中提取代码-
我创建当前主题的原因是我不明白为什么该代码会导致死锁:
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
我尝试使初始化程序成为非静态的,之后代码变得不会导致死锁。所以我相信它在某种程度上与静态 class 初始化有关。
你能解释一下吗?
回答
感谢 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();
}
}
也导致死锁
新线程正在尝试在 Lock
class 中调用 run
。该方法本身尝试创建 Lock
的新实例。在 Lock
class 完成初始化之前,它无法做到 1。 JVM 知道另一个线程已经在初始化 class,所以它会阻塞直到初始化完成。
不幸的是,由于 t.join()
调用,初始化在 run
完成之前无法完成。所以这两个线程中的每一个都需要对方取得进展才能做任何事情——死锁。
正是出于这个原因,避免在 class 初始化程序中做大量工作绝对值得。
即使 run()
方法本身为空,这一切都会发生。但它比那更糟糕 - 因为 run()
方法在创建另一个线程并等待 that 线程完成调用 run()
等之前不会完成。所以这是失败的另一个原因——它基本上会产生线程,直到 JVM 运行 资源不足。因此,即使删除类型初始值设定项也不会让您获得工作代码。
关于为什么需要类型初始化,参见section 12.4.1 of the JLS:
A class or interface type T will be initialized immediately before the first occurrence of any one of the following:
- T is a class and an instance of T is created.
- A static method declared by T is invoked.
- A static field declared by T is assigned.
- A static field declared by T is used and the field is not a constant variable (§4.12.4).
表达式 new Lock()
将始终初始化 Lock
,或等待初始化完成(当然,这可能已经发生)。
1如果您拆分 run
中的代码以便创建 Lock
的实例然后记录,然后启动线程,您你会看到它是 Lock
的创建阻塞。
我从那个答案中提取代码-
我创建当前主题的原因是我不明白为什么该代码会导致死锁:
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
我尝试使初始化程序成为非静态的,之后代码变得不会导致死锁。所以我相信它在某种程度上与静态 class 初始化有关。
你能解释一下吗?
回答
感谢 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();
}
}
也导致死锁
新线程正在尝试在 Lock
class 中调用 run
。该方法本身尝试创建 Lock
的新实例。在 Lock
class 完成初始化之前,它无法做到 1。 JVM 知道另一个线程已经在初始化 class,所以它会阻塞直到初始化完成。
不幸的是,由于 t.join()
调用,初始化在 run
完成之前无法完成。所以这两个线程中的每一个都需要对方取得进展才能做任何事情——死锁。
正是出于这个原因,避免在 class 初始化程序中做大量工作绝对值得。
即使 run()
方法本身为空,这一切都会发生。但它比那更糟糕 - 因为 run()
方法在创建另一个线程并等待 that 线程完成调用 run()
等之前不会完成。所以这是失败的另一个原因——它基本上会产生线程,直到 JVM 运行 资源不足。因此,即使删除类型初始值设定项也不会让您获得工作代码。
关于为什么需要类型初始化,参见section 12.4.1 of the JLS:
A class or interface type T will be initialized immediately before the first occurrence of any one of the following:
- T is a class and an instance of T is created.
- A static method declared by T is invoked.
- A static field declared by T is assigned.
- A static field declared by T is used and the field is not a constant variable (§4.12.4).
表达式 new Lock()
将始终初始化 Lock
,或等待初始化完成(当然,这可能已经发生)。
1如果您拆分 run
中的代码以便创建 Lock
的实例然后记录,然后启动线程,您你会看到它是 Lock
的创建阻塞。