propagacion - tipos de excepciones en java netbeans
Cómo atrapar excepciones en FutureTask (5)
Después de encontrar que FutureTask
ejecuta en un Executors.newCachedThreadPool()
en Java 1.6 (y de Eclipse) se traga las excepciones en el método Runnable.run()
, he intentado encontrar una manera de atraparlas sin agregar tiradas / atrapadas a Todas mis implementaciones de Runnable
.
La API sugiere que anular FutureTask.setException()
debería ayudar en esto:
Hace que este futuro reporte una ExecutionException con la tirable dada como su causa, a menos que este Futuro ya haya sido establecido o haya sido cancelado. Este método es invocado internamente por el método de ejecución en caso de falla del cálculo.
Sin embargo, no parece que se haya llamado a este método (al ejecutar con el depurador, FutureTask
la excepción, pero no se llama a setException
). 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 detectar las excepciones lanzadas en una tarea futura? ¿Por qué no se setException
a setException
?
También me gustaría saber por qué Thread.UncaughtExceptionHandler
no utiliza el mecanismo FutureTask
, ¿existe algún motivo para ello?
¿Has intentado usar un UncaughtExceptionHandler
?
- Necesita implementar la interfaz
UncaughtExceptionHandler
. - Para configurar
UncaughtExceptionHandler
para subprocesos de grupo, proporcione unThreadFactory
en la llamadaExecutor.newCachedThreadPool(ThreadFactory)
. - Puede establecer UncaughtExceptionHandler para el subproceso creado a través de
setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
Envíe las tareas con ExecutorService.execute
, porque solo las excepciones generadas por las tareas enviadas con execute
hacen al controlador de excepciones no capturadas. Para las tareas enviadas con ExecutorService.execute cualquier excepción lanzada se considera parte del valor de retorno de la tarea. Si una tarea enviada con el envío finaliza con una excepción, se vuelve a generar cuando se llama a Future.get
, envuelto en una ExecutionException
Hay tres formas estándar y una forma improvisada. 1. use UncaughtExceptionHandler, configure el UncaughtExceptionHandler para el hilo creado como
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable ex) {..}}
* Pero la limitación es que atrapa la excepción lanzada por hilo pero en caso de una tarea futura, se traga. 2. use afterExecute
después de crear un ejecutor de subprocesos personalizado con gancho que se haya proporcionado especialmente para este propósito. Mirando a través del código de ThreadpoolExecutor, a través de enviar> 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**();
..}
Luego, el tercero está utilizando la captura de prueba simple dentro del método de llamada, pero no puede detectar la excepción fuera de aquí.
La solución es llamar a todos los métodos de llamada desde un método de llamada de TaskFactory, una fábrica que libera callables.
He mirado el código fuente de FutureTask
y no pude encontrar dónde se llama a setException
.
Hay un método innerSetException
de FutureTask.Sync
(clase interna de FutureTask
) que se llama en caso de que un Throwable
sea lanzado por el método de ejecución. Este método también se llama en setException
.
Así que parece que el javadoc no es correcto (o muy difícil de entender ...).
Una solución mucho mejor: Java FutureTask.
Cuando llama a futureTask.get()
para recuperar el resultado del cálculo, generará una excepción (ExecutionException) si el Runnable
/ Callable
subyacente lanzó una excepción.
ExecutionException.getCause()
devolverá la excepción que el Runnable
/ Callable
lanzó.
También lanzará una excepción diferente si el Runnable
/ Callable
fue cancelado.
setException
probable que setException
no esté hecho para anular, pero se proporciona para permitirle establecer el resultado en una excepción, si fuera necesario. Lo que quieres hacer es anular el método done()
y tratar de obtener 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);
}
}
}