带有System.exit的Java静态块关闭挂钩

Raf*_*ffy 14 java static deadlock

这段代码会死锁:

public class Main {
   static public final Object a = new Object();
   static {
      Runtime.getRuntime().addShutdownHook(new Thread() {
         @Override
         public void run() { if (a == null); }
      });
      System.exit(0);
   }
   static public void main(final String[] args) {}
}
Run Code Online (Sandbox Code Playgroud)

此代码将正常退出:

public class Main {
   static public final Object a = new Object();
   static {
      final Object aa = a;
      Runtime.getRuntime().addShutdownHook(new Thread() {
         @Override
         public void run() { if (aa == null); }
      });
      System.exit(0);
   }
   static public void main(final String[] args) {}
}
Run Code Online (Sandbox Code Playgroud)

怎么了?

Tom*_*ine 14

重要的是在初始化时不同时访问类,因此保持锁定.

我想在第一种情况下发生的事情是:

  • 主线程保持初始化锁定Main.
  • 在握住锁的同时,System.exit因为它不会返回.
  • 关闭钩子执行.
  • 关闭尝试访问Main类以读取字段,但在类初始化时阻塞.

因此陷入僵局.如果你写的话,它会更清楚if (a == null);一些if (Main.a == null);.

在第二种情况下,复制该值,因此关闭钩子不需要访问Main该类.

道德:不要混合线程和类初始化.Gafter和Bloch的Java Puzzlers书有更多内容.


duf*_*ymo 1

这是死锁示例的字节码:

public class Main extends java.lang.Object{
public static final java.lang.Object a;

public Main();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   return

static {};
  Code:
   0:   new     #2; //class java/lang/Object
   3:   dup
   4:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   7:   putstatic       #3; //Field a:Ljava/lang/Object;
   10:  invokestatic    #4; //Method java/lang/Runtime.getRuntime:()Ljava/lang/Runtime;
   13:  new     #5; //class Main$1
   16:  dup
   17:  invokespecial   #6; //Method Main$1."<init>":()V
   20:  invokevirtual   #7; //Method java/lang/Runtime.addShutdownHook:(Ljava/lang/Thread;)V
   23:  iconst_0
   24:  invokestatic    #8; //Method java/lang/System.exit:(I)V
   27:  return

}
Run Code Online (Sandbox Code Playgroud)

这是正常完成的情况的字节码:

public class Main extends java.lang.Object{
public static final java.lang.Object a;

public Main();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   return

static {};
  Code:
   0:   new     #2; //class java/lang/Object
   3:   dup
   4:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   7:   putstatic       #3; //Field a:Ljava/lang/Object;
   10:  getstatic       #3; //Field a:Ljava/lang/Object;
   13:  astore_0
   14:  invokestatic    #4; //Method java/lang/Runtime.getRuntime:()Ljava/lang/Runtime;
   17:  new     #5; //class Main$1
   20:  dup
   21:  aload_0
   22:  invokespecial   #6; //Method Main$1."<init>":(Ljava/lang/Object;)V
   25:  invokevirtual   #7; //Method java/lang/Runtime.addShutdownHook:(Ljava/lang/Thread;)V
   28:  iconst_0
   29:  invokestatic    #8; //Method java/lang/System.exit:(I)V
   32:  return

}
Run Code Online (Sandbox Code Playgroud)

字节码显然不同。我要么给出答案,要么其他了解 JVM 内部结构的人会提供帮助。