Ich versuche Java zu verwenden ThreadPoolExecutor
Klasse zu verwenden, um eine große Anzahl schwerer Aufgaben mit einer festen Anzahl von Threads auszuführen. Jede der Aufgaben hat viele Stellen, an denen sie aufgrund von Ausnahmen fehlschlagen kann.
Ich habe eine Unterklasse erstellt ThreadPoolExecutor
und die afterExecute
Methode überschrieben , die alle nicht erfassten Ausnahmen bereitstellen soll, die beim Ausführen einer Aufgabe auftreten. Ich kann es jedoch nicht zum Laufen bringen.
Beispielsweise:
public class ThreadPoolErrors extends ThreadPoolExecutor {
public ThreadPoolErrors() {
super( 1, // core threads
1, // max threads
1, // timeout
TimeUnit.MINUTES, // timeout units
new LinkedBlockingQueue<Runnable>() // work queue
);
}
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if(t != null) {
System.out.println("Got an error: " + t);
} else {
System.out.println("Everything's fine--situation normal!");
}
}
public static void main( String [] args) {
ThreadPoolErrors threadPool = new ThreadPoolErrors();
threadPool.submit(
new Runnable() {
public void run() {
throw new RuntimeException("Ouch! Got an error.");
}
}
);
threadPool.shutdown();
}
}
Die Ausgabe dieses Programms lautet "Alles ist in Ordnung - Situation normal!" obwohl die einzige Runnable, die an den Thread-Pool gesendet wurde, eine Ausnahme auslöst. Irgendwelche Hinweise darauf, was hier los ist?
Vielen Dank!
Antworten:
Aus den Dokumenten :
Wenn Sie ein Runnable einreichen, wird es in eine Zukunft verpackt.
Ihr afterExecute sollte ungefähr so aussehen:
quelle
future.isDone()
? DaafterExecute
es ausgeführt wird, nachdem dasRunnable
abgeschlossen ist, gehe ich davon aus, dass esfuture.isDone()
immer zurückkehrttrue
.WARNUNG : Es ist zu beachten, dass diese Lösung den aufrufenden Thread blockiert.
Wenn Sie von der Aufgabe ausgelöste Ausnahmen verarbeiten möchten, ist es im Allgemeinen besser, diese zu verwenden
Callable
alsRunnable
.Callable.call()
Es ist erlaubt, geprüfte Ausnahmen auszulösen, und diese werden zurück an den aufrufenden Thread weitergegeben:Wenn
Callable.call()
eine AusnahmeExecutionException
ausgelöst wird , wird diese in eine eingeschlossen und von ausgelöstFuture.get()
.Dies ist wahrscheinlich einer Unterklasse vorzuziehen
ThreadPoolExecutor
. Sie haben auch die Möglichkeit, die Aufgabe erneut zu senden, wenn es sich um eine wiederherstellbare Ausnahme handelt.quelle
future.get()
oder dessen überladene Version aufgerufen wird.Die Erklärung für dieses Verhalten finden Sie im Javadoc für afterExecute :
quelle
Ich habe es umgangen, indem ich die mitgelieferte ausführbare Datei verpackt habe, die dem Testamentsvollstrecker übermittelt wurde.
quelle
whenComplete()
Methode von verbessernCompletableFuture
.Ich verwende eine
VerboseRunnable
Klasse aus jcabi-log , die alle Ausnahmen verschluckt und protokolliert. Sehr praktisch zum Beispiel:quelle
Eine andere Lösung wäre die Verwendung von ManagedTask und ManagedTaskListener .
Sie benötigen ein Callable oder Runnable, das die Schnittstelle ManagedTask implementiert .
Die Methode
getManagedTaskListener
gibt die gewünschte Instanz zurück.Und Sie implementieren in ManagedTaskListener die
taskDone
Methode:Weitere Details zum Lebenszyklus verwalteter Aufgaben und zum Listener .
quelle
Das funktioniert
Es wird ein Executor mit einem einzelnen Thread erstellt, der viele Aufgaben ausführen kann. und wartet, bis die aktuelle Ausführung beendet ist, um mit der nächsten zu beginnen
Im Falle eines Uncaugth-Fehlers oder einer Ausnahme der uncaughtExceptionHandler ihn abfangen
quelle
Wenn Sie die Ausführung einer Aufgabe überwachen möchten, können Sie 1 oder 2 Threads (je nach Auslastung möglicherweise mehr) drehen und damit Aufgaben aus einem ExecutionCompletionService-Wrapper übernehmen.
quelle
Wenn Sie
ExecutorService
von einer externen Quelle stammen (dh es ist nicht möglich, Unterklassen zu erstellenThreadPoolExecutor
und zu überschreibenafterExecute()
), können Sie einen dynamischen Proxy verwenden, um das gewünschte Verhalten zu erzielen:quelle
Dies liegt daran, dass
AbstractExecutorService :: submit
Sie wie untenrunnable
inRunnableFuture
(nichts alsFutureTask
) eingewickelt werdenDann
execute
wird es weitergeleitetWorker
undWorker.run()
wird das Folgende aufrufen.quelle
Dies ähnelt der Lösung von mmm, ist jedoch etwas verständlicher. Lassen Sie Ihre Aufgaben eine abstrakte Klasse erweitern, die die run () -Methode umschließt.
quelle
Anstatt ThreadPoolExecutor zu unterklassifizieren, würde ich eine ThreadFactory- Instanz bereitstellen , die neue Threads erstellt und ihnen einen UncaughtExceptionHandler zur Verfügung stellt
quelle