Pregunta

Después de encontrar que FutureTask se ejecuta en un Executors.newCachedThreadPool() en Java 1.6 (y desde Eclipse) se traga excepciones en el método Runnable.run(), he tratado de encontrar una manera de atrapar a estos sin la adición de tiro / catch para todas mis implementaciones Runnable.

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

  

Causas este futuro reportar una ExecutionException con el throwable dada como su causa, a no ser que este futuro ya ha sido establecido o ha sido cancelado. Este método se invoca internamente por el método de ejecución en caso de fallo de la computación.

Sin embargo, no parece ser llamado (que se ejecuta con los espectáculos del depurador se detecta la excepción por FutureTask, pero no se llama setException) este método. 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 setException ser llamado?

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

¿Fue útil?

Solución

setException probablemente no está hecho para anular, pero no se proporciona la cual permite establecer el resultado de una excepción, en caso de necesidad. 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);
        }
    }
}

Otros consejos

¿Usted ha intentado utilizar una UncaughtExceptionHandler ?

  • Es necesario poner en práctica el UncaughtExceptionHandler interfaz.
  • Para establecer una UncaughtExceptionHandler para roscas de la piscina, proporcionar un ThreadFactory en la llamada Executor.newCachedThreadPool(ThreadFactory).
  • Se puede configurar 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 las tareas presentadas con ExecutorService.submit cualquier excepción lanzada es considerado como parte del valor de retorno de la tarea. Si una tarea presentada con el presente termina con una excepción, es relanza al llamar Future.get , envuelto en una ExecutionException

Una solución mucho mejor: Java FutureTask cheque finalización

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

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

También se lanzará una excepción diferente si el Runnable / Callable fue cancelada.

He mirado en el código fuente de FutureTask y no podía encontrar donde setException está siendo llamado.
Existe un método innerSetException de FutureTask.Sync (clase interna de FutureTask) que se está llamando en caso de una Throwable ser arrojado por el método de ejecución. Este método también se denomina en setException.
Por lo tanto, costuras como el Javadoc no es correcta (o muy difícil de entender ...).

Hay tres formas estándar y de una manera improvisada.  1. El uso UncaughtExceptionHandler, ajuste el UncaughtExceptionHandler para el hilo creado como

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            public void uncaughtException(Thread t, Throwable ex) {..}}

* Sin embargo, la limitación es que captura la excepción lanzada por hilo, pero en el caso de la tarea futura, se ingiere.  2. Uso afterExecute después de hacer un threadpoolexecutor personalizada con el gancho que se ha proporcionado especialmente para este propósito. Mirando a través del código de ThreadpoolExecutor, a través de someter> ejecutar (hay un WorkQueue, workQueue.offer), las tareas se agregan a la cola de trabajo

   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. A continuación, la tercera está utilizando intento de captura sencilla dentro del método de llamada pero no se puede coger el exterior es una excepción.

  2. La solución está llamando a todos los métodos de llamada de un método llamado de un TaskFactory, una fábrica que libera callables.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top