java concurrency executor runtimeexception

java - ¿Cómo atrapar apropiadamente las Excepciones de tiempo de ejecución de los ejecutores?



concurrency executor (5)

Digamos que tengo el siguiente código:

ExecutorService executor = Executors.newSingleThreadExecutor(); executor.execute(myRunnable);

Ahora, si myRunnable arroja RuntimeExcpetion , ¿cómo puedo atraparlo? Una forma sería suministrar mi propia implementación de newSingleThreadExecutor() a newSingleThreadExecutor() y establecer custom uncaughtExceptionHandler para los Thread s que newSingleThreadExecutor() de ella. Otra forma sería ajustar myRunnable a un myRunnable local (anónimo) que contenga un try-catch -block. Tal vez hay otras soluciones similares también. Pero ... de alguna manera esto se siente sucio, siento que no debería ser tan complicado. ¿Hay una solución limpia?



Decora el ejecutable en otro ejecutable que capta las excepciones de tiempo de ejecución y las maneja:

public class REHandler implements Runnable { Runnable delegate; public REHandler (Runnable delegate) { this.delegate = delegate; } public void run () { try { delegate.run (); } catch (RuntimeException e) { ... your fancy error handling here ... } } } executor.execute(new REHandler (myRunnable));


La solución alternativa es utilizar ExecutorService.submit() lugar de execute() . Esto le devuelve un Future que puede usar para recuperar el resultado o la excepción de la tarea:

ExecutorService executor = Executors.newSingleThreadExecutor(); Runnable task = new Runnable() { public void run() { throw new RuntimeException("foo"); } }; Future<?> future = executor.submit(task); try { future.get(); } catch (ExecutionException e) { Exception rootException = e.getCause(); }


skaffman tiene razón en que usar submit es el enfoque más limpio. Un enfoque alternativo es subclase ThreadPoolExecutor y anular afterExecute(Runnable, Throwable) . Si sigue este enfoque, asegúrese de llamar a execute(Runnable) lugar de submit(Runnable) o afterExecute no se invocará.

Por la descripción de la API:

Método invocado al finalizar la ejecución del Runnable dado. Este método es invocado por el hilo que ejecutó la tarea. Si no es nulo, el Throwable es el RuntimeException o Error RuntimeException que provocó que la ejecución finalizara abruptamente.

Nota: Cuando las acciones se envuelven en tareas (como FutureTask) de forma explícita o mediante métodos como enviar, estos objetos de tareas capturan y mantienen excepciones computacionales, por lo que no causan una terminación abrupta, y las excepciones internas no se pasan a este método .


una tarea ( Callable o Runnable ) enviada a ThreadPoolExecutors se convertirá en una FuturnTask , que contiene una propiedad llamada callable igual a la tarea que envía. FuturnTask tiene su propio método de run siguiente manera. Toda excepción o c.call() en c.call() será atrapado y puesto en un outcome llamado prop. Al llamar al método de obtención de FuturnTask, se arrojará el outcome

FuturnTask.run del código fuente Jdk1.8

public void run() { ... try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; // save ex into `outcome` prop setException(ex); } if (ran) set(result); } } ... }

si quieres ver la excepción:

      1. La respuesta de skaffman
      2. sobrescribe `afterExecute` cuando usted nuevo un ThreadPoolExecutor

    @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); Throwable cause = null; if (t == null && r instanceof Future) { try { ((Future<?>) r).get(); } catch (InterruptedException | ExecutionException e) { cause = e; } } else if (t != null) { cause = t; } if (cause != null) { // log error } }