2010-05-27 10 views
105

Estoy intentando asegurarme de que mi aplicación Java toma medidas razonables para ser robusta, y parte de eso implica cerrar con gracia. Estoy leyendo sobre shutdown hooks y realmente no entiendo cómo hacer uso de ellos en la práctica.Ejemplo útil de un gancho de cierre en Java?

¿Hay algún ejemplo práctico por ahí?

Digamos que tenía una aplicación muy simple como esta a continuación, que escribe números en un archivo, 10 en una línea, en lotes de 100, y quiero asegurarme de que un lote determinado termine si el programa se interrumpe. Obtuve cómo registrar un gancho de cierre, pero no tengo idea de cómo integrar eso en mi aplicación. ¿Alguna sugerencia?

package com.example.test.concurrency; 

import java.io.File; 
import java.io.FileNotFoundException; 
import java.io.FileOutputStream; 
import java.io.PrintWriter; 

public class GracefulShutdownTest1 { 
    final private int N; 
    final private File f; 
    public GracefulShutdownTest1(File f, int N) { this.f=f; this.N = N; } 

    public void run() 
    { 
     PrintWriter pw = null; 
     try { 
      FileOutputStream fos = new FileOutputStream(this.f); 
      pw = new PrintWriter(fos); 
      for (int i = 0; i < N; ++i) 
       writeBatch(pw, i); 
     } 
     catch (FileNotFoundException e) { 
      e.printStackTrace(); 
     } 
     finally 
     { 
      pw.close(); 
     }  
    } 

    private void writeBatch(PrintWriter pw, int i) { 
     for (int j = 0; j < 100; ++j) 
     { 
      int k = i*100+j; 
      pw.write(Integer.toString(k)); 
      if ((j+1)%10 == 0) 
       pw.write('\n'); 
      else 
       pw.write(' '); 
     } 
    } 

    static public void main(String[] args) 
    { 
     if (args.length < 2) 
     { 
      System.out.println("args = [file] [N] " 
        +"where file = output filename, N=batch count"); 
     } 
     else 
     { 
      new GracefulShutdownTest1(
        new File(args[0]), 
        Integer.parseInt(args[1]) 
      ).run(); 
     } 
    } 
} 

Respuesta

134

Se podría hacer lo siguiente:

  • Vamos el gancho de cierre establecer algún AtomicBoolean (o booleano volátil) "keepRunning" false
  • (Opcionalmente, .interrupt los hilos de trabajo si esperan para datos en alguna llamada de bloqueo)
  • Espere a que los hilos de trabajo (ejecutando writeBatch en su caso) terminen, llamando al método Thread.join() en los hilos de trabajo.
  • Terminar el programa

Parte del código incompleta:

  • Añadir un static volatile boolean keepRunning = true;
  • En run() se cambia a

    for (int i = 0; i < N && keepRunning; ++i) 
        writeBatch(pw, i); 
    
  • En principal () agrega:

    final Thread mainThread = Thread.currentThread(); 
    Runtime.getRuntime().addShutdownHook(new Thread() { 
        public void run() { 
         keepRunning = false; 
         mainThread.join(); 
        } 
    }); 
    

Eso es más o menos la forma en que hago un grácil "Rechazo todos los clientes al chocar Control-C" en el terminal.


De the docs:

Cuando la máquina virtual comienza su secuencia de apagado se iniciará todos los ganchos de cierre registrados en un orden no especificado y dejar que se ejecutan simultáneamente. Cuando todos los ganchos hayan finalizado, se ejecutarán todos los finalizadores no invocados si se ha habilitado la finalización a la salida. Finalmente, la máquina virtual se detendrá.

es decir, un gancho de cierre mantiene la JVM corriendo hasta que el gancho ha terminado (volvió de la run() -method.

+11

Ah, entonces, si entiendo bien, la esencia de lo que usted dice es que el gancho de desconexión tiene la oportunidad de comunicarse con otros hilos actualmente en ejecución y detener el cierre de VM (por ejemplo, utilizando Thread.join()) hasta que esté satisfecho de que se ha completado una limpieza rápida. Ese es el punto que me estaba perdiendo. ¡Gracias! –

+1

Sí. Lo tienes. Se actualizó la respuesta con unas pocas frases de los documentos. – aioobe

+1

Lo sentimos con antelación, pero ¿no debería GracefulShutdownTest1 implementar Runnable? – Victor