Comment attraper correctement RuntimeExceptions de Huissiers?
-
18-09-2019 - |
Question
Dites que j'ai le code suivant:
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(myRunnable);
Maintenant, si myRunnable
jette un RuntimeExcpetion
, comment puis-je attraper? Une façon serait de fournir ma propre mise en œuvre de ThreadFactory
à newSingleThreadExecutor()
et mettre uncaughtExceptionHandler
s personnalisés pour les Thread
s qui sortent de celui-ci. Une autre façon serait d'envelopper myRunnable
à un Runnable
local (anonyme) qui contient un -bloc try-catch. Peut-être il y a d'autres solutions de contournement semblables aussi. Mais ... en quelque sorte cela se sent sale, je pense qu'il ne devrait pas être si compliqué. Y at-il une solution propre?
La solution
La solution propre est d'utiliser ExecutorService.submit()
au lieu de execute()
. Cela vous renvoie une Future
que vous pouvez utiliser pour récupérer le résultat ou l'exception de la tâche:
ExecutorService executor = Executors.newSingleThreadExecutor();
Runnable task = new Runnable() {
public void run() {
throw new RuntimeException("foo");
}
};
Future<?> future = executor.submit(task);
try {
future.get();
} catch (ExecutionException e) {
Exception rootException = e.getCause();
}
Autres conseils
Décorez le runnable dans un autre runnable qui attrape les exceptions d'exécution et les poignées:
public class REHandler implements Runnable {
Runnable delegate;
public REHandler (Runnable delegate) {
this.delegate = delegate;
}
public void run () {
try {
delegate.run ();
} catch (RuntimeException e) {
... your fancy error handling here ...
}
}
}
executor.execute(new REHandler (myRunnable));
Pourquoi ne pas appeler ExecutorService#submit()
, obtenir le Future
arrière et gérer vous-même des exceptions possibles lors de l'appel Future#get()
?
skaffman est correct que l'utilisation submit
est la plus propre approche. Une autre approche consiste à sous-classe ThreadPoolExecutor
et remplacer afterExecute(Runnable, Throwable)
. Si vous suivez cette approche veillez à appeler execute(Runnable)
plutôt que submit(Runnable)
ou afterExecute
ne seront pas invoquées.
Par la description de l'API:
Méthode invoquée lors de l'achèvement de exécution du Runnable donné. Cette méthode est appelée par le fil qui exécuté la tâche. Si non nul, la Throwable est le uncaught
RuntimeException
ouError
qui fait exécution de mettre fin abruptement.Remarque: lorsque les actions sont enfermés dans tâches (telles que FutureTask) soit explicitement ou via des méthodes telles que soumettre, ces objets de tâche attraper et maintenir des exceptions de calcul, et de sorte qu'ils ne causent pas de brusque la terminaison, et l'intérieur exceptions sont pas transmis à cette Procédé .
une tâche (ou Callable
Runnable
) soumis à ThreadPoolExecutors
sera convertir en FuturnTask
, contient un accessoire nommé callable
est égal à la tâche que vous soumettez. FuturnTask a sa propre méthode de run
comme suit. Toutes exception ou throwable throwed dans c.call()
seront attrapés et mis en un accessoire nommé outcome
. Lorsque vous appelez la méthode get
de FuturnTask, outcome
sera throwed
De FuturnTask.run Jdk1.8 code source
public void run() {
...
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
// save ex into `outcome` prop
setException(ex);
}
if (ran)
set(result);
}
}
...
}
si vous voulez attraper l'exception:
- 1. La réponse de skaffman
- 2. Ecraser `afterExecute` lorsque vous une nouvelle ThreadPoolExecutor
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
Throwable cause = null;
if (t == null && r instanceof Future) {
try {
((Future<?>) r).get();
} catch (InterruptedException | ExecutionException e) {
cause = e;
}
} else if (t != null) {
cause = t;
}
if (cause != null) {
// log error
}
}