J'essaie d'utiliser la ThreadPoolExecutor
classe Java pour exécuter un grand nombre de tâches lourdes avec un nombre fixe de threads. Chacune des tâches comporte de nombreux emplacements pendant lesquels elle peut échouer en raison d'exceptions.
J'ai sous ThreadPoolExecutor
- classé et j'ai remplacé la afterExecute
méthode qui est censée fournir toutes les exceptions non détectées rencontrées lors de l'exécution d'une tâche. Cependant, je n'arrive pas à le faire fonctionner.
Par exemple:
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();
}
}
La sortie de ce programme est "Tout va bien - situation normale!" même si le seul Runnable soumis au pool de threads lève une exception. Une idée de ce qui se passe ici?
Merci!
Réponses:
De la documentation :
Lorsque vous soumettez un Runnable, il sera enveloppé dans un avenir.
Votre afterExecute devrait ressembler à ceci:
la source
future.isDone()
? PuisqueafterExecute
est exécuté après laRunnable
fin, je suppose quefuture.isDone()
revient toujourstrue
.AVERTISSEMENT : il convient de noter que cette solution bloquera le thread appelant.
Si vous souhaitez traiter les exceptions levées par la tâche, il est généralement préférable d'utiliser
Callable
plutôt queRunnable
.Callable.call()
est autorisé à lever des exceptions vérifiées, et celles-ci sont propagées au thread appelant:Si
Callable.call()
lève une exception, elle sera enveloppée dans unExecutionException
et levée parFuture.get()
.Cela est probablement préférable à la sous-classification
ThreadPoolExecutor
. Il vous donne également la possibilité de soumettre à nouveau la tâche si l'exception est récupérable.la source
future.get()
ou sa version surchargée est appelée.L'explication de ce comportement se trouve dans le javadoc pour afterExecute :
la source
Je l'ai contourné en enveloppant le runnable fourni soumis à l'exécuteur testamentaire.
la source
whenComplete()
méthode deCompletableFuture
.J'utilise la
VerboseRunnable
classe de jcabi-log , qui avale toutes les exceptions et les enregistre. Très pratique, par exemple:la source
Une autre solution consisterait à utiliser ManagedTask et ManagedTaskListener .
Vous avez besoin d'un Callable ou Runnable qui implémente l'interface ManagedTask .
La méthode
getManagedTaskListener
renvoie l'instance souhaitée.Et vous implémentez dans ManagedTaskListener la
taskDone
méthode:Plus de détails sur le cycle de vie des tâches gérées et l'écouteur .
la source
Cela marche
Il créera un exécuteur avec un seul thread, qui peut obtenir beaucoup de tâches; et attendra la fin de l'exécution pour commencer avec la prochaine
En cas d'erreur ou d'exception, le uncaughtExceptionHandler l'attrapera
la source
Si vous souhaitez surveiller l'exécution de la tâche, vous pouvez faire tourner 1 ou 2 threads (peut-être plus en fonction de la charge) et les utiliser pour prendre des tâches à partir d'un wrapper ExecutionCompletionService.
la source
Si votre
ExecutorService
provient d'une source externe (c'est-à-dire qu'il n'est pas possible de sousThreadPoolExecutor
-classer et de remplacerafterExecute()
), vous pouvez utiliser un proxy dynamique pour obtenir le comportement souhaité:la source
Ceci est dû
AbstractExecutorService :: submit
est enveloppant votrerunnable
enRunnableFuture
(rienFutureTask
) comme ci - dessousEnsuite,
execute
le transmettre àWorker
etWorker.run()
appellera ci-dessous.la source
C'est similaire à la solution de mmm, mais un peu plus compréhensible. Demandez à vos tâches d'étendre une classe abstraite qui encapsule la méthode run ().
la source
Au lieu de sous-classer ThreadPoolExecutor, je lui fournirais une instance ThreadFactory qui crée de nouveaux threads et leur fournit un UncaughtExceptionHandler
la source