Cómo detectar excepciones en FutureTask
-
30-09-2019 - |
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?
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 unThreadFactory
en la llamadaExecutor.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**();
..}
-
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.
-
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.