2010-08-24 8 views
19

Après avoir constaté que FutureTask s'exécutant dans un Executors.newCachedThreadPool() sur Java 1.6 (et depuis Eclipse) avale des exceptions dans la méthode Runnable.run(), j'ai essayé de trouver un moyen de les attraper sans ajouter de jet/catch à toutes mes implémentations Runnable.Comment attraper des exceptions dans FutureTask

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é mis ou a été annulée. Cette méthode est invoquée en interne par la méthode run en cas d'é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 n'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é?

Aussi, je voudrais savoir pourquoi le mécanisme Thread.UncaughtExceptionHandler n'est pas utilisé par FutureTask, est-ce qu'il ya une raison pour cela?

+1

'setException' est bien utilisé. J'ai copié-collé votre code et cela fonctionne. Vous pouvez également essayer try-catch de 'ExecutionException' lorsque vous appelez la méthode' get() 'sur la tâche. – Kru

+0

Mauvaise façon d'utiliser le combo Callable/Future. La méthode future.get() vous fournit l'exception enveloppée dans une ExecutionException. – Bhushan

Répondre

20

setException n'est probablement pas faite pour le remplacement, mais est fournie pour vous permettre de définir le résultat d'une exception, si le besoin s'en fait sentir. Ce que vous voulez faire est de passer outre 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); 
     } 
    } 
} 
+0

Cela a fonctionné, merci. – Thirler

+0

comment nous pouvons tirer le code d'état de l'objet ExecutionException? – Min2

+0

@ Min2 Si vous voulez l'exception qui a provoqué le 'ExecutionException', vous pouvez utiliser' getCause() 'comme je le fais dans l'instruction println. – gustafc

2

J'ai regardé le code source de FutureTask et n'a pas pu trouver où setException est appelé.
Il existe une méthode innerSetException de FutureTask.Sync (classe interne de FutureTask) qui est appelée en cas de lancement d'un Throwable par la méthode d'exécution. Cette méthode est également appelée dans setException.
Donc, il semble que le javadoc n'est pas correct (ou très difficile à comprendre ...).

+0

Après avoir regardé un peu plus à la base de données terriblement lent de bogues de soleil: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6464365 Apparemment il est connu depuis 2006 et toujours pas fixé (bien qu'il soit indiqué comme fixe) – Thirler

+1

@Thirler - le rapport de bug dit clairement qu'il est corrigé ** dans Java 7 **. –

12

Avez-vous essayé d'utiliser un UncaughtExceptionHandler?

  • Vous devez implémenter l'interface UncaughtExceptionHandler.
  • Pour définir un UncaughtExceptionHandler pour les threads de pool, entrez ThreadFactory dans l'appel Executor.newCachedThreadPool(ThreadFactory).
  • Vous pouvez régler le UncaughtExceptionHandler pour le fil 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 avec 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 se termine par une exception, il est relancée lors de l'appel Future.get, enveloppé dans un ExecutionException

+0

Bonne prise! J'utilisais 'ExecutorService.submit' par erreur. – Gili

8

Une bien meilleure solution: Java FutureTask completion check

Lorsque vous appelez futureTask.get() pour récupérer le résultat du calcul, il sera renvoie une exception (ExecutionException) si le sous-jacente Runnable/Callable a levé une exception.

ExecutionException.getCause() retournera l'exception que le Runnable/Callable a lancé.

Une exception différente sera également émise si le Runnable/Callable a été annulé.

Questions connexes