2010-07-26 6 views
32

Das folgende Stück Code versucht, dies zu begleiten.Graceful Herunterfahren von Threads und Executor

Der Code wird für immer wiederholt und überprüft, ob noch ausstehende Anforderungen verarbeitet werden. Wenn es welche gibt, erzeugt es einen neuen Thread, um die Anfrage zu verarbeiten und sie an den Executor zu übergeben. Sobald alle Threads fertig sind, bleibt es für 60 Sekunden und überprü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 meisten der Verarbeitung von diesen Threads erledigt mit Datenbank. Und dieses Programm wird auf einem Windows-Rechner laufen. Was passiert mit diesen Threads, wenn jemand versucht, den Computer herunterzufahren oder sich abzumelden? Wie Sie die laufenden Threads und auch den Executor ordnungsgemäß herunterfahren?

Antwort

55

Ein typisches ordnungsgemäßes Herunterfahren eines ExecutorService könnte 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 anmelden können dass der Executor noch Aufgaben zu bearbeiten hatte, nachdem er auf die Wartezeit gewartet hatte.
** Sie können versuchen, die Worker-Threads des Executors zu zwingen, ihre aktuellen Aufgaben aufzugeben und sicherzustellen, dass sie keine der verbleibenden Threads starten.

Beachten Sie, dass die obige Lösung funktioniert, wenn ein Benutzer einen Interrupt an Ihren java Prozess ausgibt oder wenn ExecutorService nur Daemon-Threads enthält. Wenn stattdessen die ExecutorService Nicht-Daemon-Threads enthält, die nicht abgeschlossen wurden, versucht die JVM nicht, herunterzufahren, und daher werden die Shutdown-Hooks nicht aufgerufen.

Wenn Sie versuchen, einen Prozess als Teil eines diskreten Anwendungslebenszyklus herunterzufahren (kein Dienst), sollte der Shutdown-Code nicht innerhalb eines Shutdown-Hooks platziert werden, sondern an der entsprechenden Stelle, an der das Programm beendet werden soll.

+0

Das ist rückwärts. Unter der Annahme, dass 'ExecutorService' einer der typischen Werte ist, die von' Executors' Factory-Methoden zurückgegeben werden, sind die Backing-Threads Nicht-Daemon-Threads. Der Shutdown-Hook wird erst aufgerufen, wenn diese Threads beendet werden, und das wird erst geschehen, wenn der ExecutorService beendet wird. Ihre Lösung wird nur, wenn die Threads des Executors nicht Daemon sind oder wenn Ihr Programm einen Benutzerinterrupt empfängt. –

+0

Das OP fragt speziell nach dem Fall, in dem die JVM aufgefordert wurde, eine ordnungsgemäße Abschaltung durchzuführen. Unabhängig davon, der Inhalt der Antwort ist korrekt, man braucht dies nur in einem Shutdown Hook zu tun, um den Executor Service früher zu beenden. –

+0

Danke für die Antwort so schnell.Ich glaube, ich habe diesen Teil ihrer Frage übersehen. Können Sie ein drittes '***' hinzufügen, um dieses Bit zu verdeutlichen? –

7

Das Buch "Java Concurrency in Practice" heißt es:

7,4. JVM Shutdown

Die JVM kann entweder ordnungsgemäß oder abrupt in einem heruntergefahren werden. Eine geordnete Abschaltung wird eingeleitet, wenn die letzten „normal“ (nondaemon) Gewinde endet, jemand anruft System.exit, oder von anderen plattformspezifischen Mittel (wie beispielsweise ein SIGINT oder Schlagen Ctrl-C senden). [...]

7.4.1. Shutdown Hooks

Bei ordnungsgemäßer Abschaltung startet die JVM zuerst alle registrierten Shutdown-Hooks. Shutdown-Hooks sind nicht fortlaufende Threads , die mit Runtime.addShutdownHook registriert sind. Die JVM macht keine Garantien in der Reihenfolge, in der Shutdown Hooks gestartet werden. Wenn Anwendungsthreads (Daemon oder nondaemon) immer noch unter Shutdown-Zeit ausgeführt werden, führen sie weiterhin gleichzeitig mit dem Herunterfahren Prozess. Wenn alle Shutdown-Hooks abgeschlossen sind, kann die JVM Finalizer ausführen, wenn runFinalizersOnExit true ist, und dann anhält. Die JVM macht keinen Versuch, alle Anwendungsthreads zu stoppen oder zu unterbrechen, die immer noch zum Zeitpunkt des Herunterfahrens laufen; ; Sie werden abrupt beendet, wenn die JVM schließlich anhält. Wenn die Shutdown Haken oder Finalizer nicht abgeschlossen sind, dann der ordnungsgemäße Herunterfahren Prozess "hängt" und die JVM muss abrupt heruntergefahren werden. [...]

Die wichtige Bits sind, „Die JVM keinen Versuch macht keine Anwendungs-Threads zu beenden oder zu unterbrechen, die noch beim Herunterfahren Zeit ausgeführt werden;. Sie abrupt beendet werden, wenn die JVM stoppt schließlich“ also nehme ich an, dass die Verbindung zur DB abrupt beendet wird, wenn keine Shutdown-Hooks vorhanden sind, um eine graziöse 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, usw. die Zeit abläuft, wenn die App läuft. ist ohne solche Haken beendet.

2

können Sie entweder rufen shutdown() auf dem ExecutorService:

Leitet ein ordnungsgemäßes Abschalten, in dem zuvor eingereichten Aufgaben ausgeführt sind, aber keine neuen Aufgaben akzeptiert.

oder Sie können shutdownNow() nennen:

Versuche, alle aktiv Ausführung von Aufgaben zu stoppen, hält die Verarbeitung des Wartens Aufgaben und gibt eine Liste der Aufgaben, die Ausführung erwarteten.

Es gibt keine Garantien über Best-Effort versucht, Verarbeitung aktiv ausgeführte Aufgaben zu stoppen. Zum Beispiel werden typische Implementierungen über Thread.interrupt() abgebrochen, so jede Aufgabe, die nicht auf Interrupts reagiert möglicherweise nie beenden.

Welche Sie hängt nennen, wie schlecht Sie es wollen stoppen ....

+0

Ich bin mit dem folgenden Problem in meiner Web-App.Wie Speicherlecks zu stoppen .http: //stackoverflow.com/questions/39166512/memory-leak-error-in-tomcat-server-even-after-removed-quartz -related-code – Vicky

5

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

+1

Dennoch ist es ein wenig problematisch, da 'MoreExecutors.getExitingExecutorService' nur' ThreadPoolExecutor'-Instanzen akzeptiert. Daher müssen Sie diese Executoren manuell definieren, anstatt einfach "Executors.newFixedThreadPool" aufzurufen, was "ExecutorService" zurückgibt. – voo