Рычаг завершения работы статического блока Java с System.exit

Этот код зайдет в тупик:

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) {}
}

Этот код будет обычно выходить:

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) {}
}

Что происходит?

14
задан Raffy 16 February 2010 в 02:31
поделиться

2 ответа

Важно, чтобы классы не использовались одновременно во время инициализации, поэтому блокировка сохраняется.

Я предполагаю, что в первом случае происходит следующее:

  • Главный поток удерживает блокировку инициализации для Main .
  • Удерживая блокировку, System.exit блокируется, поскольку не возвращается.
  • Выполняется ловушка выключения.
  • Завершение работы пытается получить доступ к классу Main для чтения поля, но блокируется по мере инициализации класса.

Отсюда и тупик. Будет немного понятнее, если вы напишете if (a == null); как if (Main.a == null); .

Во втором случае значение копируется, и поэтому ловушка выключения не нуждается в доступе к классу Main .

Мораль: не смешивайте потоки и инициализацию класса. В книге Гафтера и Блоха «Java-головоломки» об этом есть больше.

14
ответ дан 1 December 2019 в 13:59
поделиться

Вот байт-код для примера взаимоблокировки:

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

}

А вот байт-код для случая, который заканчивается нормально:

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

}

Байт-код явно отличается. Я либо найду ответ, либо мне поможет тот, кто разбирается во внутреннем устройстве JVM.

1
ответ дан 1 December 2019 в 13:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: