为什么这段代码会产生死锁?

11
class A {
    static final int i;
    static {
        i = 128;

        Thread t = new Thread() {
            public void run() {
                System.out.println("i=" + i);
            }
        };
        t.start();
        try {
           t.join();
        } catch (InterruptedException e) {
           Thread.currentThread().interrupt();
        }
    }
}
public class MainTesting {


    public static void main(String[] args) {
        A a = new A();
        System.out.println("finish");
    }
}

我从未看到finish被打印出来或者i的值。这是为什么呢?

2个回答

13

您开始在线程1(即“主”线程)上启动,并开始执行A类的静态初始化程序。

在该静态初始化程序中,您随后启动一个新线程(2),该线程使用A类中的某些内容。这意味着线程2需要等待A类完成初始化才能继续进行,根据JLS的第12.4.2节

如果C的Class对象指示由其他线程正在进行C的初始化,则释放LC并阻止当前线程,直到被通知正在进行的初始化已完成为止,然后重复此步骤。

但是,A类的静态初始化程序会等待线程2完成(通过调用join()),然后它自己才能完成,导致死锁:静态初始化程序无法完成,直到线程2完成,而线程2无法完成,直到静态初始化程序完成...

总之,不要这样做:)


2
请不要介意,但我认为您已经在http://stackoverflow.com/questions/6686333/deadlock-caused-by-thread-join-in-a-static-block中回答了这个问题。 - MickJ
1
@MickJ:是的,我往往发现很难记得我在过去两个月甚至过去两年中回答的所有问题 :) - Jon Skeet
我能理解。成为Jon Skeet的负担 :) 但无论如何我还是喜欢它。像我这样的新手在得到答案而不是愤怒的踩和“在发布之前做些研究”的评论时会感到更受欢迎。我喜欢这种方式+1 - MickJ

6

类和静态块的加载都是隐式同步的。这意味着在初始化期间,在另一个线程中无法访问类中的任何内容。在这种情况下,初始化正在等待使用 A.i 的线程。换句话说,它正在等待第一个线程完成静态块。

注意:它不使用普通锁,而且该线程声称处于可运行状态,即使它已经死锁。

2013-06-21 11:20:40
Full thread dump Java HotSpot(TM) 64-Bit Server VM (23.21-b01 mixed mode):

"Thread-0" prio=6 tid=0x000000000d55d000 nid=0x3cc4 in Object.wait() [0x000000000dbdf000]
   java.lang.Thread.State: RUNNABLE
    at Main$1.run(Main.java:14) <- where A.i is referenced.

"main" prio=6 tid=0x00000000022df000 nid=0x3284 in Object.wait() [0x000000000257e000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000007d5610448> (a Main$1)
    at java.lang.Thread.join(Thread.java:1258)
    - locked <0x00000007d5610448> (a Main$1)
    at java.lang.Thread.join(Thread.java:1332)
    at Main.<clinit>(Main.java:19)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:188)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:113)

"静态块隐式同步,同步发生在对象上,对吧?但是静态块可能没有与之关联的任何对象。" - Sunny
静态块与 ClassName.class 对象相关联。如果您使用 static synchronized 方法,则锁定的就是该对象。我不知道类加载是否以这种方式实现,或者类加载器上是否有锁定或一些全局锁定。我怀疑这取决于 JVM。 - Peter Lawrey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接