2014-12-18 2 views
12

(Используете ли статические инициализаторы в Java является хорошей идеей, выходит за рамки данного вопроса.)Как диагностировать или обнаружить тупики в Java статические инициализаторы

Я встречая тупики в моем приложении Scala, которые я считаю вызванных блокировкой статических инициализаторов в скомпилированных классах.

Мой вопрос заключается в том, как обнаружить и диагностировать эти взаимоблокировки. Я обнаружил, что обычные инструменты JVM для взаимоблокировок не работают, когда задействуются блоки статического инициализатора.

Вот простой пример Java приложение, которое ТУПИКИ в статическом инициализаторе:

public class StaticDeadlockExample implements Runnable 
{ 
    static 
    { 
     Thread thread = new Thread(
       new StaticDeadlockExample(), 
       "StaticDeadlockExample child thread"); 
     thread.start(); 
     try { 
      thread.join(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 

    public static void main(String[] args) 
    { 
     System.out.println("in main"); 
    } 

    public static void sayHello() 
    { 
     System.out.println("hello from thread " + Thread.currentThread().getName()); 
    } 

    @Override 
    public void run() { 
     StaticDeadlockExample.sayHello(); 
    } 
} 

Если запустить это приложение, оно ТУПИКИ. Трассировки стеки во время тупика (из jstack) содержат следующие два зашедших в тупике темы:

"StaticDeadlockExample child thread" prio=6 tid=0x000000006c86a000 nid=0x4f54 in Object.wait() [0x000000006d38f000] 
    java.lang.Thread.State: RUNNABLE 
    at StaticDeadlockExample.run(StaticDeadlockExample.java:37) 
    at java.lang.Thread.run(Thread.java:619) 

    Locked ownable synchronizers: 
    - None 

"main" prio=6 tid=0x00000000005db000 nid=0x2fbc in Object.wait() [0x000000000254e000] 
    java.lang.Thread.State: WAITING (on object monitor) 
    at java.lang.Object.wait(Native Method) 
    - waiting on <0x000000004a6a7870> (a java.lang.Thread) 
    at java.lang.Thread.join(Thread.java:1143) 
    - locked <0x000000004a6a7870> (a java.lang.Thread) 
    at java.lang.Thread.join(Thread.java:1196) 
    at StaticDeadlockExample.<clinit>(StaticDeadlockExample.java:17) 
    at java.lang.Class.forName0(Native Method) 
    at java.lang.Class.forName(Class.java:169) 
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:116) 

    Locked ownable synchronizers: 
    - None 

Моими вопросов является

  1. Почему первый поток помечен как Runnable, когда это на самом деле ожидая на замке? Могу ли я каким-то образом определить «реальное» состояние этого потока?
  2. Почему ни один поток не помечен как имеющий какие-либо (соответствующие) блокировки, когда на самом деле один удерживает статический блокиратор intializer, а другой ждет его? Могу ли я каким-то образом обнаружить блокировку статического инициализатора?
+0

Почему, на ваш взгляд, это ожидание на замке? Если он ожидал блокировки, он (1) находился бы в состоянии WAITING, а не RUNNABLE и (2), упомянул бы «« Ожидание на ... »или« Ожидание блокировки »после верхней записи стека. – Dima

+3

Я не думаю [эти блокировки инициализации] (http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html # jls-12.4.2). Они управляются JVM для этого процесса. Возможно, свалки потоков не раскрывают их. –

+0

@ Dima - Я знаю, что он ждет блокировки , потому что я построил код так, чтобы он был. Вышеупомянутая программа будет зависеть навсегда, если вы ее запустите - попробуйте сами. Вы считаете, что он не использует 'java.util.concurrent.locks'? Если так - это правильно, но не очень полезно. – Rich

ответ

1

Scala позволяет легко попасть в ловушку.

Легкое обходное решение или диагностика (если вы видите клинику в трассировке стека) - это позволить вашему объекту расширить приложение, чтобы DelayedInit удалил ваш код из статического инициализатора.

Некоторые поясняющие ссылки:

https://issues.scala-lang.org/browse/SI-7646

Scala: Parallel collection in object initializer causes a program to hang

http://permalink.gmane.org/gmane.comp.lang.scala.user/72499

+0

Спасибо. Вы знаете, можно ли обнаружить эти взаимоблокировки? Нити перечислены как RUNNABLE, а неявная статическая блокировка не указана в замках, принадлежащих потокам. – Rich

1

Я попробовал этот пример с my tool и он также терпит неудачу в обнаружении этого в тупик. После нескольких попыток с отладчиком jconsole и повторного запуска примера пару раз я заметил, что начальный поток отмечен как RUNNABLE, потому что он запущен, проблема в том, что с момента запуска потока доступа к статическому члену эта операция помещается в очередь после завершения статического блока инициализатора (эта семантика не ясна в JVM specification, однако, похоже, это так).

Статический инициализатор не заканчивается, потому что в этом причудливом примере операция соединения заставляет его ждать завершения потока, однако я замечаю, что эта операция «в очереди» не захватывает блокировку явно или неявно в соответствии со спецификацией JVM. Возможно, это не следует рассматривать как тупик per se, поскольку это был бы тот же случай, если тело метода run содержит бесконечный цикл.

+1

Ну, это не задокументировано как блокировка, но он действует точно так же, как блокировка. Я думаю, что это «секретный» внутренний замок, который JVM не позволяет пользователю взаимодействовать или проверять. Я думаю, что было бы обманом определить проблему, сказав, что, поскольку она не задокументирована как блокировка, она не может быть тупиком. – Rich

+1

«начальный поток отмечен как RUNNABLE, потому что он запущен» - он не запускается в обычном значении слова, он блокируется, ожидая блокировки скрытого статического инициализатора. Мой вопрос: 1) как я могу обнаружить, что поток находится в этом состоянии, и 2) как я могу определить, какие статические инициализаторы он ждет? Я сильно подозреваю, что ответы на оба являются «вы не можете, поскольку JVM не раскрывает эту информацию» – Rich

Смежные вопросы