Detección de punto muerto en Java

Hace mucho tiempo, guardé una oración de un libro de referencia de Java: “Java no tiene ningún mecanismo para manejar el punto muerto, ni siquiera sabrá que se produjo un punto muerto”. (Head First Java 2nd Edition, p.516)

Entonces, ¿qué es eso? ¿Hay alguna manera de atrapar un caso de estancamiento en Java? Quiero decir, ¿hay alguna manera en que nuestro código comprenda que se ha producido un caso de estancamiento?

Desde JDK 1.5, existen métodos muy útiles en el paquete java.lang.management para encontrar e inspeccionar los lockings que se producen. Consulte el findMonitorDeadlockedThreads() y findDeadlockedThreads() de la clase ThreadMXBean .

Una posible forma de usar esto es tener un hilo de vigilancia separado (o tarea periódica) que lo haga.

Código de muestra:

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

JConsole puede detectar interlockings en una aplicación en ejecución.

JDK 5 y 6 volcarán la información de locking retenido en un volcado de hilo completo (obtenido con kill -3, jstack, jconsole, etc.). JDK 6 incluso contiene información sobre ReentrantLock y ReentrantReadWriteLock. A partir de esta información, es posible diagnosticar un interlocking encontrando un ciclo de locking: el subproceso A mantiene el locking 1, el subproceso B mantiene el locking 2 y A solicita 2 o B lo solicita 1. Desde mi experiencia, esto suele ser bastante obvio.

Otras herramientas de análisis realmente pueden encontrar puntos muertos potenciales, incluso si no ocurren. Las herramientas de subprocesos de proveedores como OptimizeIt, JProbe, Coverity, etc. son buenos lugares para buscar.

Tenga en cuenta que hay un tipo de punto muerto utilizando el paquete simultáneo que es muy difícil de depurar. Aquí es donde tiene un ReentrantReadWriteLock y un hilo toma el locking de lectura y luego (por ejemplo) intenta ingresar a un monitor sostenido por otro hilo que también está esperando agarrar el locking de escritura. Lo que lo hace especialmente difícil de depurar es que no hay ningún registro de quién ha ingresado un locking de lectura. Es simplemente un conteo. El hilo incluso podría haber arrojado una excepción y haber muerto dejando el recuento de lecturas distinto de cero.

Aquí hay un interlocking de muestra que el método findDeadlockedThreads mencionado anteriormente no obtendrá:

 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!"); } } } } 

En general, java no ofrece detección de punto muerto. La palabra clave sincronizada y los monitores incorporados hacen que sea más difícil razonar sobre el punto muerto que en los lenguajes con locking explícito.

Sugeriría migrar a java.util.concurrent.Lock lockings y similares para hacer que sus esquemas de locking sean más fáciles de razonar. De hecho, puede realizar fácilmente su propia implementación de la interfaz de locking con la detección de punto muerto. El algoritmo consiste básicamente en atravesar el gráfico de dependencia de locking y buscar un ciclo.

Si está en Java 5, puede llamar al método findMonitorDeadlockedThreads() en ThreadMXBean, que puede obtener a través de una llamada de java.lang.management.ManagementFactory.getThreadMXBean() . Esto encontrará lockings ocasionados por monitores de objeto solamente. En Java 6 hay findDeadlockedThreads() que también encontrará deadlocks causados ​​por “sincronizadores ReentrandLock (por ejemplo, ReentrandLock y ReentrantReadWriteLock ).

Tenga en cuenta que probablemente sea costoso llamar a estos métodos, por lo que deben usarse solo con fines de resolución de problemas.

No es exactamente lo que pediste, pero cuando ocurre un interlocking, puedes hacer un “kill -3” en la identificación del proceso y volcar un volcado de hilo en stdout. Además, el 1.6 jvm tiene algunas herramientas para hacer lo mismo de manera guiada.

Si se está ejecutando desde la línea de comandos y sospecha que está estancado, intente ctrl + break en windows (ctrl + \ en unix) para obtener un volcado de subprocesos. Ver http://java.sun.com/javase/6/webnotes/trouble/TSG-VM/html/gbmps.html

El Dr. Heinz Kabutz de JavaSpecialists ha escrito un boletín entretenido e informativo sobre locking de Java y describe algo llamado ThreadMXBean en otro boletín informativo . Entre ellos, debe tener una buena idea de los problemas y algunos consejos para hacer su propia instrumentación.

Java puede detectar interlockings (aunque no en tiempo de ejecución, aún puede diagnosticarlo e informarlo).

Por ejemplo, cuando se usa una versión ligeramente modificada del código ‘Saurabh M. Chande’ a continuación (se cambió a Java y se agregó un poco de tiempo para garantizar un locking en cada ejecución). Una vez que lo ejecutas y bloquea, si escribes:

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

Generará un volcado de stack, que incluirá la siguiente información:

 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" 

Los lockings se pueden evitar si sigue una regla simple: hacer que todos los hilos reclamen y liberar sus lockings en el mismo orden. De esta forma, nunca se llega a una situación en la que pueda producirse un punto muerto.

Incluso el problema de los filósofos del comedor puede verse como una violación de esta regla, ya que utiliza conceptos relativos de cuchara izquierda y derecha que dan lugar a diferentes hilos utilizando diferentes órdenes de asignación de las cucharas. Si las cucharas estuvieran numeradas de forma única y todos los filósofos trataran de obtener primero la cuchara con el número más bajo, el punto muerto sería imposible.

En mi opinión, prevenir es mejor que curar.

Esta es una de las dos pautas que me gusta seguir para garantizar que los hilos funcionen correctamente. El otro es asegurarse de que cada hilo sea el único responsable de su propia ejecución, ya que es el único plenamente consciente de lo que está haciendo en cualquier momento.

Así que eso significa que no Thread.stop llamadas Thread.stop , use un indicador global (o cola de mensajes o algo así) para decirle a otro hilo que desea que se tomen medidas. Entonces deja que ese hilo haga el trabajo real.

Si está depurando en eclipse, puede pausar la aplicación (seleccione la aplicación en la vista de depuración y el pequeño || botón en la barra de herramientas de depuración) y luego puede informar interlockings.

Consulte http://runnerwhocodes.blogspot.com/2007/10/deadlock-detection-with-eclipse.html para ver un ejemplo.

Después de tanto tiempo, puedo escribir el ejemplo más simple de Deadlock. Los comentarios son bienvenidos

 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(); } } 

Java 5 introdujo ThreadMXBean, una interfaz que proporciona diversos métodos de supervisión para hilos. … La diferencia es que findDeadlockedThreads también puede detectar interlockings causados ​​por lockings de propietario (java.util.concurrent), mientras que findMonitorDeadlockedThreads solo puede detectar lockings de monitor (es decir, bloques sincronizados)

O puede detectarlo programáticamente, consulte esto https://dzone.com/articles/how-detect-java-deadlocks

tienes que modificar el código un poco en la clase Deadlock

    Deadlock () {
     Therad t = new Thread (esto);  // modificado
     t.start ();
     System.out.println ();  // cualquier instrucción para retrasar
     a.methodA (b);
 }

Además, el código anterior no siempre causará un locking muerto, solo algunas veces puede suceder.