2010-08-24 12 views
19

Después de encontrar que FutureTask ejecutándose en un Executors.newCachedThreadPool() en Java 1.6 (y desde Eclipse) se traga excepciones en el método Runnable.run(), he intentado encontrar una forma de detectar estos sin agregar throw/catch a todas mis implementaciones Runnable.Cómo detectar excepciones en FutureTask

La API sugiere que anulando FutureTask.setException() debería ayudar en esto:

Causas este futuros reportar una ExecutionException con el throwable dada como su causa, a no ser que este futuro ya se ha establecido o se ha cancelado. Este método es invocado internamente por el método de ejecución al fallar el cálculo.

Sin embargo, no parece que este método sea llamado (que se ejecuta con el depurador muestra se detecta la excepción por FutureTask, pero no setException se llama). He escrito el siguiente programa para reproducir mi problema:

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); 
    } 
} 

Mi pregunta principal es: ¿Cómo puedo capturar excepciones lanzadas en un FutureTask? ¿Por qué no se llama a setException?

También me gustaría saber por qué el mecanismo Thread.UncaughtExceptionHandler no es utilizado por FutureTask, ¿hay alguna razón para esto?

+1

'setException' está bien utilizado. Copié y pegué tu código y funciona. También es posible que desee probar el try-catch de 'ExecutionException' al llamar al método' get() 'en la tarea. – Kru

+0

Forma incorrecta de usar el combo Llamado/Futuro. El método future.get() proporciona la excepción envuelta en una ExecutionException. – Bhushan

Respuesta

20

setException probablemente no está hecho para anulación, pero se proporciona para permitirle establecer el resultado a una excepción, en caso de ser necesario. Lo que se quiere hacer es reemplazar el método done() y tratar de conseguir el resultado:

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

Esto funcionó, gracias. – Thirler

+0

cómo podemos extraer el código de estado del objeto ExecutionException? – Min2

+0

@ Min2 si desea la excepción que provocó la 'ExecutionException', puede usar' getCause() 'como lo hago en la instrucción println. – gustafc

2

He consultado el código fuente de FutureTask y no he podido encontrar dónde se llama al setException.
Hay un método innerSetException de FutureTask.Sync (clase interna de FutureTask) al que se llama en el caso de un Throwable lanzado por el método de ejecución. Este método también se llama en setException.
Por lo tanto, parece que el javadoc no es correcto (o muy difícil de entender ...).

+0

Después de mirar un poco más en la base de datos terriblemente lenta de bugs: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6464365 Aparentemente se conoce desde 2006 y todavía no se ha corregido (aunque se indica como fijo)) – Thirler

+1

@Thirler: el informe de error dice claramente que está arreglado ** en Java 7 **. –

12

¿Ha intentado utilizar un UncaughtExceptionHandler?

  • Debe implementar la interfaz UncaughtExceptionHandler.
  • Para establecer un UncaughtExceptionHandler para hilos de grupo, proporcione un ThreadFactory en la llamada Executor.newCachedThreadPool(ThreadFactory).
  • puede establecer el UncaughtExceptionHandler para el hilo creado a través de setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)

Presentar las tareas con ExecutorService.execute, ya que sólo las excepciones lanzadas desde las tareas presentadas con execute hacen al controlador de excepciones. Para Tareas enviadas con ExecutorService.submit, cualquier excepción lanzada se considera parte del valor de retorno de la tarea.Si una tarea presentada con presentar termina con una excepción, se relanza al llamar Future.get, envuelto en una ExecutionException

+0

¡Buena captura! Estaba usando 'ExecutorService.submit' por error. – Gili

8

Una solución mucho mejor: Java FutureTask completion check

Cuando se llama a futureTask.get() para recuperar el resultado del cálculo que lo hará lanzar una excepción (ExecutionException) si el Runnable/Callable subyacente lanzó una excepción.

ExecutionException.getCause() devolverá la excepción que arrojó el Runnable/Callable.

También arrojará una excepción diferente si se canceló Runnable/Callable.

Cuestiones relacionadas