web-dev-qa-db-ger.com

Das Programm wird nicht sofort beendet, wenn alle ExecutorService-Aufgaben abgeschlossen sind

Ich habe eine Reihe von ausführbaren Objekten in einen ExecutorService gestellt:

// simplified content of main method
ExecutorService threadPool = Executors.newCachedThreadPool();
for(int i = 0; i < workerCount; i++) {
    threadPool.execute(new Worker());
}

Ich würde erwarten, dass mein Programm/Prozess sofort beendet wird, nachdem alle Mitarbeiter fertig sind. Laut meinem Protokoll dauert es jedoch noch 20 bis 30 Sekunden, bis dies geschieht. Die Arbeiter stellen keine Ressourcen zur Verfügung, tatsächlich tun sie im Moment nichts.

Versteht mich nicht falsch, das ist kein entscheidendes Problem für mich, ich versuche nur zu verstehen, was passiert, und frage mich, ob dies normal ist.

22
alapeno

Executors.newCachedThreadPool() verwendet Executors.defaultThreadFactory() für seine ThreadFactory. Die Javadocs von defaultThreadFactory sagen, dass "jeder neue Thread als non-daemon - Thread erstellt wird" (Hervorhebung hinzugefügt). Daher sind die für newCachedThreadPool erstellten Threads kein Daemon. Das bedeutet, dass sie verhindern, dass die JVM auf natürliche Weise beendet wird (mit "natürlich" meine ich, dass Sie weiterhin System.exit(1) aufrufen können oder das Programm beenden können, um die JVM anzuhalten).

Der Grund, warum die App überhaupt beendet wird, ist, dass jeder Thread, der innerhalb der newCachedThreadPool erstellt wurde, das Zeitlimit überschreitet und sich nach einiger Zeit der Inaktivität schließt. Wenn sich der letzte von ihnen selbst schließt und in Ihrer Anwendung keine Nicht-Daemon-Threads mehr vorhanden sind, wird er beendet.

Sie können (und sollten) die ExecutorService manuell über shutdown oder shutdownNow schließen.

Siehe auch JavaDoc für Thread , das über Daemon-Ness spricht.

28
yshavit

Ich würde erwarten, dass mein Programm/Prozess sofort beendet wird, nachdem alle Mitarbeiter fertig sind. Laut meinem Protokoll dauert es jedoch noch 20 bis 30 Sekunden, bis dies geschieht. Die Arbeiter stellen keine Ressourcen zur Verfügung, tatsächlich tun sie im Moment nichts.

Das Problem ist, dass Sie Ihre ExecutorService nicht herunterfahren. Nachdem Sie alle Aufträge an den Dienst übergeben haben, sollten Sie den Dienst herunterfahren. Andernfalls wird die JVM nicht beendet, es sei denn, alle Threads darin sind Daemon-Threads. Wenn Sie den Thread-Pool nicht herunterfahren, beenden alle mit ExecutorService verknüpften Threads (falls noch kein Daemon) die JVM am Ende. Wenn Sie Aufgaben an einen zwischengespeicherten Thread-Pool übermittelt haben, müssen Sie auf das Zeitlimit der Threads warten und ernten, bevor die JVM beendet wird.

ExecutorService threadPool = Executors.newCachedThreadPool();
for(int i = 0; i < workerCount; i++) {
    threadPool.execute(new Worker());
}
// you _must_ do this after submitting all of your workers
threadPool.shutdown();

Das Starten der Threads als Daemon ist höchstwahrscheinlich nicht das, was Sie tun möchten, weil Ihre Anwendung vor stoppt - die Aufgaben abgeschlossen sind und alle Aufgaben sofort beendet werden. Ich habe gerade ein kurzes Audit durchgeführt und von den 178 Mal, in denen wir ExecutorService-Klassen in unserem Produktionscode verwenden, wurden nur zwei von ihnen als Daemon-Threads gestartet. Der Rest ist ordnungsgemäß heruntergefahren.

Wenn Sie eine ExecutorService zum Beenden zwingen müssen, wenn die Anwendung beendet wird, ist die Verwendung von shutdownNow() mit der korrekten Behandlung der Thread-Interrupt-Flags in Ordnung.

6
Gray

Aus dem javadoc für Executors.newCachedThreadPool():

Threads, die seit 60 Sekunden nicht verwendet wurden, werden beendet und aus dem Cache entfernt.

Es ist normalerweise eine gute Idee, shutdown() für eine ExecutorService aufzurufen, wenn Sie wissen, dass keine neuen Aufgaben übergeben werden. Dann werden alle Aufgaben in der Warteschlange abgeschlossen, der Dienst wird jedoch sofort heruntergefahren.

(Wenn Sie sich nicht darum kümmern, ob alle Tasks abgeschlossen sind, beispielsweise wenn Hintergrundberechnungen nicht relevant sind, wenn Ihre Hauptbenutzeroberfläche nicht mehr vorhanden ist), können Sie eine ThreadFactory erstellen, mit der alle Threads in diesem Pool festgelegt werden Daemon.)

3

Grundsätzlich rufen Sie bei einem ExecutorService den Befehl shutdown () auf und erwarten Sie waitTermination ():

ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
   taskExecutor.execute(new MyTask());
}
taskExecutor.shutdown();
try {
  taskExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
  ...
}
3
Danke Xie

Für Multi-Threading von ExecutorService Lösung ist

0
Venkat Alugolu