为什么从静态初始化程序启动线程并等待其完成会导致死锁?

gst*_*low 2 java concurrency multithreading deadlock static-initialization

我从那个答案中获取代码-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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我试图启动它很多时间,并且总是导致死锁。

输出始终相同:

1# Getting ready to greet the world
1# before lock creation
1# after lock creation
1# awaiting thread finish
13# Started thread
Run Code Online (Sandbox Code Playgroud)

我试图使初始化程序变为非静态,并且在此之后代码不会导致死锁。因此,我认为它与静态类初始化相关。
你能解释一下吗?

回答

感谢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 Code Online (Sandbox Code Playgroud)

也会导致僵局

Jon*_*eet 8

新线程正在尝试runLock类中调用。该方法本身会尝试创建的新实例Lock。在类完成初始化之前,它不能执行1Lock。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是阻塞的创建。