Nützliches Beispiel für einen Shutdown-Hook in Java?


125

Ich versuche sicherzustellen, dass meine Java-Anwendung angemessene Schritte unternimmt, um robust zu sein. Dazu gehört auch das ordnungsgemäße Herunterfahren. Ich lese über Shutdown-Hooks und verstehe nicht, wie ich sie in der Praxis einsetzen kann.

Gibt es da draußen ein praktisches Beispiel?

Angenommen, ich hatte eine wirklich einfache Anwendung wie die folgende, die Zahlen in eine Datei schreibt, 10 in eine Zeile, in Stapeln von 100, und ich möchte sicherstellen, dass ein bestimmter Stapel beendet wird, wenn das Programm unterbrochen wird. Ich kann einen Shutdown-Hook registrieren, habe aber keine Ahnung, wie ich ihn in meine Anwendung integrieren kann. Irgendwelche Vorschläge?

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

Antworten:


160

Sie können Folgendes tun:

  • Lassen Sie den Shutdown-Hook einen AtomicBoolean (oder flüchtigen Boolean) "keepRunning" auf false setzen
  • (Optional .interruptdie Arbeitsthreads, wenn sie in einem blockierenden Aufruf auf Daten warten)
  • Warten Sie, bis die Arbeitsthreads ( writeBatchin Ihrem Fall ausgeführt) abgeschlossen sind, indem Sie die Thread.join()Methode für die Arbeitsthreads aufrufen .
  • Beenden Sie das Programm

Einige skizzenhafte Codes:

  • Füge hinzu ein static volatile boolean keepRunning = true;
  • In run () wechseln Sie zu

    for (int i = 0; i < N && keepRunning; ++i)
        writeBatch(pw, i);
  • In main () fügen Sie hinzu:

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

So mache ich ungefähr ein elegantes "Alle Clients ablehnen, wenn ich Control-C drücke" im Terminal.


Aus den Dokumenten :

Wenn die virtuelle Maschine mit dem Herunterfahren beginnt, werden alle registrierten Shutdown-Hooks in einer nicht festgelegten Reihenfolge gestartet und gleichzeitig ausgeführt. Wenn alle Hooks abgeschlossen sind, werden alle nicht aufgerufenen Finalizer ausgeführt, wenn Finalization-on-Exit aktiviert wurde. Schließlich wird die virtuelle Maschine angehalten.

Das heißt, ein Shutdown-Hook hält die JVM so lange am Laufen, bis der Hook beendet wurde (von der run () -Methode zurückgegeben).


14
Ah - wenn ich das richtig verstehe, ist der Kern Ihrer Aussage, dass der Shutdown-Hook die Möglichkeit hat, mit anderen derzeit laufenden Threads zu kommunizieren und das Herunterfahren der VM zu verzögern (z. B. mithilfe von Thread.join () ) bis sichergestellt ist, dass eine schnelle Bereinigung abgeschlossen ist. Das ist der Punkt, den ich vermisst habe. Vielen Dank!
Jason S

2
Ja. Du hast es. Die Antwort wurde mit einigen Sätzen aus den Dokumenten aktualisiert.
Aioobe

1
Entschuldigung im Voraus, aber sollte GracefulShutdownTest1 Runnable nicht implementieren?
Victor

Funktioniert dies auch, wenn eine GUI im Hauptthread gestartet wurde? Ich weiß, dass dies nicht der beste Weg ist (verwenden Sie stattdessen EDT), aber ich arbeite an altem Code.
Agostino

1
@ MartynasJusevičius, die JVM wird beendet, wenn alle Nicht-Daemon-Threads beendet wurden. Wenn mainThreades sich um einen Daemon-Thread handelt, wird joingarantiert, dass wir darauf warten, dass er beendet wird, bevor die JVM heruntergefahren wird.
Aioobe

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.