Обнаружение взаимоблокировки в Java

Давным-давно я сохранил предложение из справочника Java: "Java не имеет механизма для обработки тупика, он даже не знает, что произошел тупик". (Head First Java 2nd Edition, стр .516)

Итак, что это? Есть ли способ поймать тупик в Java? Я имею в виду, есть ли способ, которым наш код понимает случай блокировки?

Ответ 1

Так как JDK 1.5 есть очень полезные методы в пакете java.lang.management для поиска и проверки тупиков, которые происходят. См. Метод findMonitorDeadlockedThreads() и findDeadlockedThreads() класса ThreadMXBean.

Возможный способ использования этого - иметь отдельный сторожевой поток (или периодическую задачу), который делает это.

Пример кода:

  ThreadMXBean tmx = ManagementFactory.getThreadMXBean();
  long[] ids = tmx.findDeadlockedThreads();
  if (ids != null) {
     ThreadInfo[] infos = tmx.getThreadInfo(ids, true, true);
     System.out.println("The following threads are deadlocked:");
     for (ThreadInfo ti : infos) {
        System.out.println(ti);
     }
  }

Ответ 2

JConsole способен обнаруживать взаимоблокировки в запущенном приложении.

Ответ 3

JDK 5 и 6 сбрасывают данные блокировки в полной дампе потока (полученные с помощью kill -3, jstack, jconsole и т.д.). JDK 6 содержит информацию о ReentrantLock и ReentrantReadWriteLock. Из этой информации можно диагностировать тупик, обнаружив цикл блокировки: Нить A удерживает блокировку 1, Thread B удерживает блокировку 2, и либо A запрашивает 2, либо B запрашивает 1. Из моего опыта это обычно довольно очевидно.

Другие инструменты анализа могут действительно находить потенциальные блокировки, даже если они не встречаются. Инструменты резьбы от таких поставщиков, как OptimizeIt, JProbe, Coverity и т.д. - это хорошие места для поиска.

Ответ 4

Обратите внимание, что существует тупик с использованием параллельного пакета, который очень трудно отлаживать. Вот где у вас есть ReentrantReadWriteLock, и один поток захватывает блокировку чтения, а затем (скажем) пытается войти в монитор, удерживаемый другим потоком, который также ждет захвата блокировки записи. Особенно сложно отладить, что нет записи о том, кто ввел блокировку чтения. Это просто счет. Нить могла даже вызвать исключение и умерла, оставив отсчет чтения ненулевым.

Вот пример тупика, который ранее не найден метод findDeadlockedThreads:

import java.util.concurrent.locks.*;
import java.lang.management.*;

public class LockTest {

    static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public static void main(String[] args) throws Exception {
        Reader reader = new Reader();
        Writer writer = new Writer();
        sleep(10);
        System.out.println("finding deadlocked threads");
        ThreadMXBean tmx = ManagementFactory.getThreadMXBean();
        long[] ids = tmx.findDeadlockedThreads();
        if (ids != null) {
            ThreadInfo[] infos = tmx.getThreadInfo(ids, true, true);
            System.out.println("the following threads are deadlocked:");
            for (ThreadInfo ti : infos) {
                System.out.println(ti);
            }
        }
        System.out.println("finished finding deadlocked threads");
    }

    static void sleep(int seconds) {
        try {
            Thread.currentThread().sleep(seconds*1000);
        } catch (InterruptedException e) {}
    }

    static class Reader implements Runnable {
        Reader() {
            new Thread(this).start();
        }
        public void run() {
            sleep(2);
            System.out.println("reader thread getting lock");
            lock.readLock().lock();
            System.out.println("reader thread got lock");
            synchronized (lock) {
                System.out.println("reader thread inside monitor!");
                lock.readLock().unlock();
            }
        }
    }

    static class Writer implements Runnable {
        Writer() {
            new Thread(this).start();
        }
        public void run() {
            synchronized (lock) {
                sleep(4);
                System.out.println("writer thread getting lock");
                lock.writeLock().lock();
                System.out.println("writer thread got lock!");
            }
        }
    }
}

Ответ 5

В общем, java не обеспечивает обнаружение блокировки. Синхронизированное ключевое слово и встроенные мониторы затрудняют рассуждение о тупике, чем в языках с явной блокировкой.

Я бы предложил перейти к использованию java.util.concurrent.Lock locks и тому подобное, чтобы упростить ваши схемы блокировки. На самом деле вы можете легко сделать свою собственную реализацию интерфейса блокировки с обнаружением тупиковой ситуации. Алгоритм состоит в том, чтобы в основном пройти график зависимости блокировки и искать цикл.

Ответ 6

Если вы находитесь на Java 5, вы можете вызвать метод findMonitorDeadlockedThreads() на ThreadMXBean, который вы можете получить через вызов java.lang.management.ManagementFactory.getThreadMXBean(). Это обнаружит блокировки, вызванные только объектными мониторами. На Java 6 есть findDeadlockedThreads(), который также найдет взаимоблокировки, вызванные "собственными синхронизаторами" (например, ReentrandLock и ReentrantReadWriteLock).

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

Ответ 7

Не совсем то, что вы просили, но когда возникает тупиковая ситуация, вы можете сделать "kill -3" на идентификаторе процесса, и он выгружает дамп потока в stdout. Кроме того, у 1,6 jvm есть некоторые инструменты, чтобы делать то же самое в gui-манере.

Ответ 8

Если вы работаете из командной строки, и вы подозреваете, что вы зашли в тупик, попробуйте ctrl + break в windows (ctrl +\in unix), чтобы получить дамп потока. См. http://java.sun.com/javase/6/webnotes/trouble/TSG-VM/html/gbmps.html

Ответ 9

р. Хайнц Кабуц из JavaSpecialists написал интересную и информативную > о тупиках Java и описывает что-то, называемое ThreadMXBean в еще одна проблема с выпуском бюллетеней. Между ними вы должны получить хорошее представление о проблемах и о некоторых указателях на выполнение собственных инструментов.

Ответ 10

Тупиков можно избежать, если следовать простому правилу: попросите все потоки заявить и отпустите свои блокировки в том же порядке. Таким образом, вы никогда не попадаете в ситуацию, когда может возникнуть тупик.

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

По-моему, профилактика лучше, чем лечение.

Это одно из двух рекомендаций, которые мне нравятся, чтобы обеспечить правильную работу потоков. Другой - обеспечить, чтобы каждый поток был исключительно ответственным за его собственное исполнение, поскольку он полностью знает, что он делает в любой момент времени.

Таким образом, это означает, что нет вызовов Thread.stop, используйте глобальный флаг (или очередь сообщений или что-то в этом роде), чтобы сообщить другому потоку, что вы хотите предпринять. Затем пусть этот поток выполнит фактическую работу.

Ответ 11

Java может обнаруживать взаимоблокировки (хотя не во время выполнения, он все равно может диагностировать и сообщать об этом).

Например, при использовании слегка модифицированной версии кода "Saurabh M. Chande" (изменил его на Java и добавил некоторое время, чтобы гарантировать блокировку при каждом запуске). После того, как вы запустите его и заблокируете, если вы наберете:

kill -3 PID   # where 'PID' is the Linux process ID

Он создаст дамп стека, который будет содержать следующую информацию:

Found one Java-level deadlock:
=============================
"Thread-0":
     waiting to lock monitor 0x08081670 (object 0x7f61ddb8, a Deadlock$A),
     which is held by "main"
"main":
      waiting to lock monitor 0x080809f0 (object 0x7f61f3b0, a Deadlock$B),
      which is held by "Thread-0"

Ответ 12

Если вы отлаживаете затмение, вы можете приостановить приложение (выберите приложение в окне отладки и маленькую кнопку на панели инструментов отладки), а затем он может сообщать о блокировках.

См. http://runnerwhocodes.blogspot.com/2007/10/deadlock-detection-with-eclipse.html для примера.

Ответ 13

После столь долгого времени я могу написать простейший пример Deadlock. Комментарии приветствуются.

Class A
{
  synchronized void methodA(B b)
  {
    b.last();
  }

  synchronized void last()
  {
    SOP(" Inside A.last()");
  }
}

Class B
{
  synchronized void methodB(A a)
  {
    a.last();
  }

  synchronized void last()
  {
    SOP(" Inside B.last()");
  }
}


Class Deadlock implements Runnable 
{
  A a = new A(); 
  B b = new B();

  // Constructor
  Deadlock()
  {
    Thread t = new Thread(); 
    t.start();
    a.methodA(b);
  }

  public void run()
  {
    b.methodB(a);
  }

  public static void main(String args[] )
  {
    new Deadlock();
  }
}

Ответ 14

В Java 5 представлен ThreadMXBean - интерфейс, который предоставляет различные методы мониторинга потоков.... Разница заключается в том, что findDeadlockedThreads также может обнаруживать взаимоблокировки, вызванные блокировками владельца (java.util.concurrent), в то время как findMonitorDeadlockedThreads может обнаруживать блокировки монитора (т.е. Синхронизированные блоки)

Или вы можете обнаружить его программно, обратитесь к https://dzone.com/articles/how-detect-java-deadlocks

Ответ 15

вам нужно немного изменить код в классе Deadlock

   Deadlock() {
    Therad t = new Thread(this); // modified
    t.start();
    System.out.println(); //any instruction to delay
    a.methodA(b);
}

Также указанный код не всегда вызывает мертвую блокировку, только несколько раз это может произойти.