Comment attraper des exceptions dans FutureTask
-
30-09-2019 - |
Question
Après avoir constaté que FutureTask
en cours d'exécution dans un Executors.newCachedThreadPool()
sur Java 1.6 (et d'Eclipse) engloutit des exceptions dans la méthode Runnable.run()
, j'ai essayé de trouver un moyen d'attraper ces sans ajouter un jet / catch à toutes mes mises en œuvre Runnable
.
L'API suggère que l'annulation de FutureTask.setException()
devrait contribuer à ceci:
Causes ce futur signaler un ExecutionException avec le throwable donné que sa cause, à moins que cet avenir a déjà été défini ou a été annulée. Cette méthode est appelée en interne par la méthode d'exécution lors de l'échec du calcul.
Cependant, cette méthode ne semble pas être appelé (en cours d'exécution avec le débogueur montre l'exception est interceptée par FutureTask
, mais setException
est pas appelé). J'ai écrit le programme suivant pour reproduire mon problème:
public class RunTest {
public static void main(String[] args) {
MyFutureTask t = new MyFutureTask(new Runnable() {
@Override
public void run() {
throw new RuntimeException("Unchecked exception");
}
});
ExecutorService service = Executors.newCachedThreadPool();
service.submit(t);
}
}
public class MyFutureTask extends FutureTask<Object> {
public MyFutureTask(Runnable r) {
super(r, null);
}
@Override
protected void setException(Throwable t) {
super.setException(t);
System.out.println("Exception: " + t);
}
}
Ma principale question est: Comment puis-je attraper des exceptions jetées dans un FutureTask? Pourquoi ne pas setException
appelée?
Je voudrais aussi savoir pourquoi le mécanisme de Thread.UncaughtExceptionHandler
n'est pas utilisé par FutureTask
, est-il raison?
La solution
setException
est probablement pas fait pour des raisons impérieuses, mais est fourni pour vous permettre de définir le résultat à une exception, le cas échéant. Qu'est-ce que vous voulez faire est de substituer la méthode done()
et essayer d'obtenir le résultat:
public class MyFutureTask extends FutureTask<Object> {
public MyFutureTask(Runnable r) {
super(r, null);
}
@Override
protected void done() {
try {
if (!isCancelled()) get();
} catch (ExecutionException e) {
// Exception occurred, deal with it
System.out.println("Exception: " + e.getCause());
} catch (InterruptedException e) {
// Shouldn't happen, we're invoked when computation is finished
throw new AssertionError(e);
}
}
}
Autres conseils
Avez-vous essayé d'utiliser un UncaughtExceptionHandler
?
- Vous devez mettre en œuvre le
UncaughtExceptionHandler
Interface . - Pour définir un
UncaughtExceptionHandler
pour les fils de la piscine, fournir unThreadFactory
dans l'appelExecutor.newCachedThreadPool(ThreadFactory)
. - Vous pouvez définir le UncaughtExceptionHandler pour le thread créé par
setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
Soumettre les tâches avec ExecutorService.execute
, car seules exceptions lancées des tâches soumises avec execute
font au gestionnaire d'exception non interceptée. Pour les tâches soumises à ExecutorService.submit
toute exception levée est considérée comme faisant partie de la valeur de retour de la tâche. Si une tâche soumise à soumettre à une exception se termine, il est relancée lorsque vous appelez Future.get
, enveloppé dans un ExecutionException
Une bien meilleure solution: Java FutureTask contrôle d'achèvement
Lorsque vous appelez futureTask.get()
pour récupérer le résultat du calcul, il lancera une exception (ExecutionException) si le Runnable
/ Callable
sous-jacente a lancé une exception.
ExecutionException.getCause()
renverra l'exception que le Runnable
/ Callable
jeté.
Il sera également lancer une exception différente si la Runnable
/ Callable
a été annulée.
J'ai regardé le code source de FutureTask
et n'a pas pu trouver où setException
est appelé.
Il existe une méthode de innerSetException
de FutureTask.Sync
(classe interne de FutureTask
) qui est appelé dans le cas d'un Throwable
être lancée par la méthode d'exécution. Cette méthode est aussi appelée à setException
.
Donc, il coutures comme le javadoc n'est pas correct (ou très difficile à comprendre ...).
Il existe trois façons standard et d'une manière improvisée. 1. Utilisation UncaughtExceptionHandler, régler le UncaughtExceptionHandler pour le fil créé comme
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable ex) {..}}
* Mais la limitation est qu'il attrape l'exception lancée par fil, mais dans le cas de la tâche future, elle est avalée.
2. L'utilisation afterExecute
après avoir fait un ThreadPoolExecutor personnalisé avec crochet qui a été spécialement prévu à cet effet. En regardant à travers le code de ThreadPoolExecutor, via soumettre> execute (il y a un WorkQueue, workQueue.offer
), les tâches sont ajoutées à la file d'attente de travail
final void runWorker(Worker arg0) {
Thread arg1 = Thread.currentThread();
Runnable arg2 = arg0.firstTask;
..
while(arg2 != null || (arg2 = this.**getTask()**) != null) {
arg0.lock();
..
try {
this.beforeExecute(arg1, arg2);
Object arg4 = null;
try {
arg2.run();
} catch (RuntimeException arg27) {
..
} finally {
this.**afterExecute**(arg2, (Throwable)arg4);
}
}
getTask() {..
this.workQueue.**poll**();
..}
-
Ensuite, le troisième est l'utilisation simple prise d'essai dans la méthode d'appel, mais vous ne pouvez pas attraper l'exception en dehors ici.
-
La solution de contournement appelle toutes les méthodes d'appel d'une méthode d'appel d'un TaskFactory, une usine qui appelables libère.