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?

Était-ce utile?

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 un ThreadFactory dans l'appel Executor.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**();
..}
  1. 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.

  2. 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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top