Estoy frente a un problema al convertir el código para no bloqueo código usando CompletableFuture. Para minimizar el alcance de la cuestión, he creado un código de ejemplo que se comporta de forma diferente cuando se utiliza CompletableFuture. La cuestión es CompletableFuture traga la excepción de Ejecutable-delegación.
Estoy usando delegación en la parte superior del Ejecutable y ExecutorService proporcionar alguna código de contenedor requerido en mi solicitud original.
Código de muestra:
MyRunnable: Mi ejecutable muestra, que siempre se produce la excepción.
public class MyRunnable implements Runnable { @Override public void run() { System.out.println("This is My Thread throwing exception : " + Thread.currentThread().getName()); throw new RuntimeException("Runtime exception from MyThread"); } }
DelegatingRunnable - esto está delegando ejecutable que los delegados y la lógica envoltura alrededor del Ejecutable pasaron a él, y marcador de posición para el manejo de excepciones.
public class DelegatingRunnable implements Runnable { private Runnable delegate; public DelegatingRunnable(Runnable delegate) { this.delegate = delegate; } @Override public void run() { System.out.println("Delegating Thread start : " + Thread.currentThread().getName()); try { // Some code before thread execution delegate.run(); // Some code after thread execution } catch (Exception e) { // While using CompletableFuture, could not catch exception here System.out.println("###### Delegating Thread Exception Caught : " + Thread.currentThread().getName()); //throw new RuntimeException(e.getMessage()); } catch (Throwable t) { System.out.println("!!!!!!! Delegating Thread Throwable Caught : " + Thread.currentThread().getName()); } System.out.println("Delegating Thread ends : " + Thread.currentThread().getName()); } }
DelegatingExecutorService - esta delegados ejecutar el método. Simplemente envuelve el ejecutable con DelegatingRunnable.
public class DelegatingExecutorService extends AbstractExecutorService { private ExecutorService executor; public DelegatingExecutorService(ExecutorService executor) { this.executor = executor; } @Override public void execute(Runnable command) { executor.execute(new DelegatingRunnable(command)); } // Othere delegating methods }
MainClass - Estoy utilizando dos enfoques. WAY1 - usando ExecutorService sin CompletableFuture. Way2 - usando CompletableFuture
public class MainClass { public static void main(String[] arg) { //way1(); way2(); } public static void way2() { System.out.println("Way:2 # This is main class : " + Thread.currentThread().getName()); ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()+1); DelegatingExecutorService executorService = new DelegatingExecutorService(executor); CompletableFuture.runAsync(new MyRunnable(), executorService) .whenComplete((res, ex) -> { if (ex != null) { System.out.println("whenComplete - exception : " + Thread.currentThread().getName()); } else { System.out.println("whenComplete - success : " + Thread.currentThread().getName()); } }); executor.shutdown(); System.out.println("main class completed : " + Thread.currentThread().getName()); } public static void way1() { System.out.println("Way:1 # This is main class : " + Thread.currentThread().getName()); ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()+1); DelegatingExecutorService executorService = new DelegatingExecutorService(executor); executorService.execute(new MyRunnable()); executor.shutdown(); System.out.println("main class completed : " + Thread.currentThread().getName()); } }
Pregunta: Cuando ejecuto WAY1 (), la salida es
Way:1 # This is main class : main
Delegating Thread start : pool-1-thread-1
This is My Thread throwing exception : pool-1-thread-1
###### Delegating Thread Exception Caught : pool-1-thread-1
main class completed : main
Delegating Thread ends : pool-1-thread-1
Se puede observar que el bloque de captura de 'DelegatingRunnable' puede detectar la excepción aquí, que se levantó de MyRunnable. Pero si uso Way2 () usando CompletableFuture, a excepción de MyRunnable no es cought bajo DelegatingRunnable, aunque veo que es ser la tos bajo 'whenComplete' de devolución de llamada de CompletableFuture.
Salida del Way2 es
Way:2 # This is main class : main
Delegating Thread start : pool-1-thread-1
This is My Thread throwing exception : pool-1-thread-1
Delegating Thread ends : pool-1-thread-1
whenComplete - exception : main
main class completed : main
Se puede notar que la CompletableFuture está utilizando el mismo DelegatingExecutionService y DelegatingRunnable internamente. No entiendo por qué DelegatingRunnable no puede detectar la excepción en este caso.
(¿Por qué estoy usando CompletableFuture -? Esto es sólo un código de ejemplo para explicar el problema exacto que estoy enfrentando Pero en general, es necesario utilizar CompletableFuture para hacer la cadena de tarea evantually en forma no bloqueante.)
En el código fuente de la CompletableFuture
que se puede ver que envuelve a la dada Runnable
en un objeto del tipo AsyncRun
que a su vez pone en práctica Runnable
. Esta AsyncRun
será pasado al ejecutor de su execute
método. Cuando el interno / original, Runnable
se produce una excepción, se ve atrapado por el código de AsyncRun
y la CompletableFuture
se completa como fracasado, pero será la excepción no ser re-lanzado.
Es por eso que el envoltorio ( DelegatingRunnable
) nunca verá la excepción.