Как обнаружить тупик? Тайм-аут в синхронизированном блоке?

Im отлаживает приложение Java, которое запускает несколько потоков. После некоторого времени просмотра журнала кажется, что один из этих потоков больше не работает. Я предполагаю, что поток ожидает блокировки, которая никогда не выпускается (последний вывод перед вызовом синхронизированного метода).

Могу ли я настроить тайм-аут на поток; своего рода "ждать этого замка, но если он не будет доступен через 10 секунд, не ждите больше!"

Ответ 1

Вместо встроенных блокировок Object вы можете использовать java.util.concurrent.Lock. RentrantLock без справедливого упорядочения имеет такое же основное поведение и семантику, что и внутренняя блокировка. Существует метод tryLock, который принимает параметр таймаута:

Lock lock = ...;
if (lock.tryLock(10L, TimeUnit.SECONDS)) {
    try {
        // manipulate protected state
    } finally {
        lock.unlock();
    }
} else {
      // perform alternative actions
}

Ответ 2

Вместо добавления дополнительного кода для отладки вы можете использовать инструмент отладки или профилировщик.

Один из вариантов заключается в использовании чего-то вроде JConsole (поставляется с JDK), который включает в себя кнопку "Detect Deadlock" (по крайней мере, на Java 6, я не думаю, что она работает на Java 5). Другой вариант - создать дамп потока в консоли - в Unix вы можете ввести "kill -3", а в Windows CTRL + BRK будет работать. Другие инструменты профилирования, такие как VisualVM (также в JDK), могут помочь. Наконец, JCarder, который является "инструментом с открытым исходным кодом для поиска возможных взаимоблокировок в параллельных многопоточных Java-программах".

Ответ 3

Вы можете иметь потоки совместно с явным блокировкой (см. java.util.concurrent.lock.Lock). Затем вы можете использовать Lock.tryLock(), который может принимать дополнительный тайм-аут.

Вы также можете использовать утилиту jstack, которая поставляется с java 1.6 (не уверен около 1,5), которая будет распечатывать состояние всех ваших потоков и то, что они могут или не могут ждать. Просто назовите его идентификатором процесса. например.

  > jstack PID 

        "Signal Dispatcher" daemon prio=10 tid=0x00000000408e8400 nid=0x79a8 runnable [0x0000000000000000..0x000000004143f810]
           java.lang.Thread.State: RUNNABLE

        "Finalizer" daemon prio=10 tid=0x00000000408c9400 nid=0x79a7 in Object.wait() [0x0000000041a7b000..0x0000000041a7bb00]
           java.lang.Thread.State: WAITING (on object monitor)
            at java.lang.Object.wait(Native Method)
            - waiting on <0x00007f992d1e7050> (a java.lang.ref.ReferenceQueue$Lock)
            at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116)
            - locked <0x00007f992d1e7050> (a java.lang.ref.ReferenceQueue$Lock)
            at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132)
            at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159)

        "Reference Handler" daemon prio=10 tid=0x00000000408c2000 nid=0x79a6 in Object.wait() [0x000000004197a000..0x000000004197ac80]
           java.lang.Thread.State: WAITING (on object monitor)
            at java.lang.Object.wait(Native Method)
            - waiting on <0x00007f992d41a958> (a java.lang.ref.Reference$Lock)
            at java.lang.Object.wait(Object.java:485)
            at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116)
            - locked <0x00007f992d41a958> (a java.lang.ref.Reference$Lock)

Ответ 4

Вы не можете использовать таймауты с традиционно синхронизированными методами. Однако, используя "новый" материал java.util.concurrent, вы можете использовать программные блокировки с поддержкой тайм-аута.

Например, посмотрите java.util.concurrent.locks.ReentrantLock

Повторное взаимное исключение Блокировка с одно и то же базовое поведение и семантика при доступе к неявной блокировке монитора используя синхронизированные методы и заявления, но с расширенными возможности.

Ответ 5

Могут быть две причины: 1) Нить умерла 2) Нить заблокирована где-то или делает то, чего вы не ожидали.

Лучшее решение - всегда использовать отладчик (дожидаться, пока ситуация не возникнет, а затем проложить приложения) или использовать JConsole/JStack/JVisualVM.

Ответ 6

В то время как при синхронизации синхронизированных методов происходит тайм-ауты, их реализация громоздка. В основном вы бы породили поток таймера, который прерывал бы поток блоков после T секунд... не приятно.

Если вы используете Java 5 или выше, я настоятельно рекомендую вам ознакомиться с тем, что предлагают новые классы concurrency. Например, вы можете использовать ReentrantLock, который имеет метод tryLock (long timeout, TimeUnit unit), который позволит вам попытаться получить блокировку, но позволяет вам сбежать через определенное время.