La manera correcta de coger RuntimeExceptions de los Ejecutores?
-
18-09-2019 - |
Pregunta
Decir que tengo el siguiente código:
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(myRunnable);
Ahora, si myRunnable
lanza un RuntimeExcpetion
, cómo puedo alcanzarlo?Una forma sería la de proporcionar mi propio ThreadFactory
aplicación a newSingleThreadExecutor()
y configurar de forma personalizada uncaughtExceptionHandler
s para la Thread
s que salen de ella.De otra manera sería envolver myRunnable
a un local (anónimo) Runnable
que contiene un try-catch -bloque.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?
Solución
La solución limpia es utilizar ExecutorService.submit()
en lugar de execute()
. Esto le devuelve una Future
que se puede utilizar para recuperar el resultado o 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();
}
Otros consejos
Decorar el ejecutable en otro ejecutable que atrapa las excepciones de tiempo de ejecución y los 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));
¿Por qué no llamar ExecutorService#submit()
, obtener la Future
atrás y luego manejar las posibles excepciones a sí mismo al llamar Future#get()
?
skaffman es correcto en que el uso de submit
es el enfoque más limpio. Un enfoque alternativo es una subclase ThreadPoolExecutor
y anular afterExecute(Runnable, Throwable)
. Si sigue este enfoque asegúrese de llamar execute(Runnable)
en lugar de no ser invocados submit(Runnable)
o afterExecute
.
Por la descripción de la API:
método invocado una vez finalizado ejecución de lo dado Ejecutable. Esta método es invocado por el hilo que ejecutado la tarea. Si no es nulo, el Throwable es la no detectada
RuntimeException
oError
la causada la ejecución de terminar abruptamente.Nota: Cuando las acciones están encerrados en tareas (tales como FutureTask) o bien explícitamente o por medio de métodos tales como presentar, estos objetos de la tarea y atrapan mantener excepciones computacionales, y de modo que no causen abrupta terminación, y la interna excepciones son no pasaron a esta método .
una tarea(Callable
o Runnable
enviadas a ThreadPoolExecutors
será convertir a un FuturnTask
, contiene una proposición con nombre callable
es igual a la tarea de enviar.FuturnTask tiene su propio run
método de la siguiente manera.Toda excepción o arrojar tirado en c.call()
será apresado y puesto en un accesorio llamado outcome
.Cuando se llama a FuturnTask del get
método, outcome
será lanzado
FuturnTask.ejecutar Desde Jdk1.8 Código Fuente
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 desea capturar la excepción :
- 1.skaffman la respuesta
- 2.sobreescribir `afterExecute` cuando un nuevo 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
}
}