web-dev-qa-db-de.com

Anmutiges Herunterfahren von Threads und Executor

Der folgende Code versucht dies zu begleiten.

Der Code wird für immer durchlaufen und prüft, ob noch ausstehende Anforderungen zur Verarbeitung vorliegen. Falls vorhanden, wird ein neuer Thread erstellt, um die Anforderung zu verarbeiten, und an den Executor übermittelt. Wenn alle Threads fertig sind, bleibt der Computer für 60 Sekunden im Ruhezustand und prüft erneut auf ausstehende Anforderungen.

public static void main(String a[]){
    //variables init code omitted
    ExecutorService service = Executors.newFixedThreadPool(15);
    ExecutorCompletionService<Long> comp = new ExecutorCompletionService<Long>(service);
    while(true){
        List<AppRequest> pending = service.findPendingRequests();
        int noPending = pending.size();
        if (noPending > 0) {
            for (AppRequest req : pending) {
                Callable<Long> worker = new RequestThread(something, req);
                comp.submit(worker);
            }
        }
        for (int i = 0; i < noPending; i++) {
            try {
                Future<Long> f = comp.take();
                long name;
                try {
                    name = f.get();
                    LOGGER.debug(name + " got completed");
                } catch (ExecutionException e) {
                    LOGGER.error(e.toString());
                }
            } catch (InterruptedException e) {
                LOGGER.error(e.toString());
            }
        }
        TimeUnit.SECONDS.sleep(60);
    }

  }

Meine Frage ist die meiste Verarbeitung, die von diesen Threads mit der Datenbank erledigt wird. Und dieses Programm läuft auf einem Windows-Rechner. Was passiert mit diesen Threads, wenn jemand versucht, die Maschine herunterzufahren oder abzumelden? Wie kann man die laufenden Threads und auch den Executor ordnungsgemäß herunterfahren?

33
user378101

Ein typisches ordnungsgemäßes Herunterfahren eines ExecutorService könnte in etwa wie folgt aussehen:

final ExecutorService executor;

Runtime.getRuntime().addShutdownHook(new Thread() {
    public void run() {
        executor.shutdown();
        if (!executor.awaitTermination(SHUTDOWN_TIME)) { //optional *
            Logger.log("Executor did not terminate in the specified time."); //optional *
            List<Runnable> droppedTasks = executor.shutdownNow(); //optional **
            Logger.log("Executor was abruptly shut down. " + droppedTasks.size() + " tasks will not be executed."); //optional **
        }
    }
});

* Sie können protokollieren, dass der Executor nach dem Abwarten der Wartezeit noch Aufgaben zu bearbeiten hatte.
** Sie können versuchen, die Worker-Threads des Executors dazu zu zwingen, ihre aktuellen Aufgaben abzubrechen und sicherzustellen, dass sie keine der verbleibenden starten.

Beachten Sie, dass die Lösung oben funktioniert, wenn ein Benutzer einen Interrupt für Ihren Java-Prozess ausgibt oder wenn Ihre ExecutorService nur Daemon-Threads enthält. Wenn stattdessen ExecutorService Nicht-Daemon-Threads enthält, die noch nicht abgeschlossen sind, versucht die JVM nicht, das System herunterzufahren, und daher werden die Shutdown-Hooks nicht aufgerufen.

Wenn Sie versuchen, einen Prozess als Teil eines diskreten Anwendungslebenszyklus (nicht eines Dienstes) herunterzufahren, sollte der Code zum Herunterfahren nicht in einem Shutdown-Hook, sondern an der entsprechenden Stelle platziert werden, an der das Programm beendet werden soll.

58
Tim Bender

Das Buch " Java Concurrency in Practice " besagt:

7.4. JVM Herunterfahren

Die JVM kann entweder in einem .__ heruntergefahren werden. ordentlich oder abrupt. Ein ordentlicher Das Herunterfahren wird eingeleitet, wenn das letzte "normaler" (nondaemon) Thread beendet, jemand ruft System.exit auf, oder durch andere plattformspezifische Mittel (z. B. Senden eines SIGINT oder Drücken von Strg-C). [...]

7.4.1. Haken herunterfahren

Bei einem ordnungsgemäßen Herunterfahren der JVM zuerst startet alle registrierten Shutdown-Hooks . Shutdown-Hooks sind nicht gestartete Threads die bei .__ registriert sind. Runtime.addShutdownHook. Die JVM macht Keine Garantie für die Reihenfolge, in der Abschalthaken werden gestartet. Wenn überhaupt Anwendungs-Threads (Daemon oder nondaemon) laufen noch um Abschaltzeit laufen sie weiter gleichzeitig mit dem Herunterfahren verarbeiten. Wenn alle Shutdown-Hooks abgeschlossen, kann die JVM die Ausführung von .__ wählen. Finalisierer, wenn runFinalizersOnExit .__ ist. stimmt und hält dann an. Die JVM macht keine versuchen, eine beliebige .__ zu stoppen oder zu unterbrechen. Anwendungsthreads, die immer noch __ sind. läuft beim Herunterfahren; Sie sind abrupt beendet, wenn die JVM hält schließlich an. Wenn das Herunterfahren Hooks oder Finalizer werden nicht abgeschlossen, dann das geordnete Herunterfahren "hängt" und die JVM muss heruntergefahren werden plötzlich. [...]

Die wichtigen Bits sind "Die JVM versucht nicht, Anwendungsthreads zu stoppen oder zu unterbrechen, die zum Zeitpunkt des Herunterfahrens noch ausgeführt werden. Sie werden abrupt beendet, wenn die JVM schließlich stoppt." Ich nehme an, die Verbindung zur DB wird abrupt abgebrochen, wenn keine Shutdown-Hooks vorhanden sind, um eine ordentliche Bereinigung durchzuführen (wenn Sie Frameworks verwenden, stellen sie normalerweise solche Shutdown-Hooks bereit). Meiner Erfahrung nach kann die Sitzung mit der DB so lange bestehen bleiben, bis die DB das Zeitlimit überschritten hat. wird ohne solche Haken beendet. 

7
Enno Shioji

Da das Hinzufügen eines Shutdown-Hooks zum expliziten Aufruf von shutdown() für mich nicht funktioniert hat, habe ich eine einfache Lösung in Guaves Guava gefunden: com.google.common.util.concurrent.MoreExecutors.getExitingExecutorService .

5
Georgi Peev

Sie können entweder shutdown() über die ExecutorService aufrufen:

Leitet ein ordnungsgemäßes Herunterfahren ein, bei dem zuvor übermittelte Aufgaben sind ausgeführt, aber keine neuen Aufgaben werden akzeptiert.

oder Sie können shutdownNow() anrufen:

Versucht alle aktiv zu stoppen das Ausführen von Aufgaben stoppt die Verarbeitung von wartenden Aufgaben und gibt eine Liste zurück von den Aufgaben, die auf uns warteten Ausführung.

Über .__ hinaus gibt es keine Garantien. Best-Effort-Versuche zu stoppen Bearbeitung aktiv ausgeführter Aufgaben . Zum Beispiel typische Implementierungen wird über Thread.interrupt () abgebrochen, also jede Aufgabe, die nicht auf .__ reagiert. Unterbrechungen werden möglicherweise niemals beendet.

Welches Sie anrufen, hängt davon ab, wie stark Sie wollen, dass es aufhört ...

2
skaffman

Ich hatte ein ähnliches Problem, ich verwende, um Fehler zu bekommen

o.a.c.loader.WebappClassLoaderBase :: Die Webanwendung [ROOT] scheint einen Thread mit dem Namen [pool-2-thread-1] gestartet zu haben, konnte diesen jedoch nicht stoppen. Dies führt sehr wahrscheinlich zu einem Speicherverlust. Stack-Trace des Threads: Sun.misc.Unsafe.park (native Methode) Java.util.concurrent.locks.LockSupport.park (LockSupport.Java:175) Java.util.concurrent.locks.AbstractQueuedSynchronizer $ ConditionObject.await (AbstractQueuedSynchronizer.Java:2039)

Gebrüllter Code reparierte es

private ThreadPoolExecutor executorPool;

@PostConstruct
public void init() {
    log.debug("Initializing ThreadPoolExecutor");
    executorPool = new ThreadPoolExecutor(1, 3, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1));
}

@PreDestroy
public void destroy() {
    log.debug("Shuting down ThreadPoolExecutor");
    executorPool.shutdown();
}
0
Chinmay Samant