Come catturare correttamente RuntimeExceptions da Esecutori?
-
18-09-2019 - |
Domanda
Dire che ho il seguente codice:
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(myRunnable);
Ora, se myRunnable
lancia un RuntimeExcpetion
, come posso prenderlo? Un modo potrebbe essere quello di fornire la mia implementazione ThreadFactory
per newSingleThreadExecutor()
e impostare uncaughtExceptionHandler
s personalizzati per le Thread
s che vengono fuori di esso. Un altro modo sarebbe quello di avvolgere myRunnable
ad un Runnable
locale (anonimo) che contiene un -block try-catch. Forse ci sono altre soluzioni simili anche. Ma ... in qualche modo questo si sente sporca, ritengo che non dovrebbe essere questo complicato. C'è una soluzione pulita?
Soluzione
La soluzione pulita è quella di utilizzare ExecutorService.submit()
invece di execute()
. In questo modo si restituisce un Future
che è possibile utilizzare per recuperare il risultato o eccezione del compito:
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();
}
Altri suggerimenti
Decorare la eseguibile in un altro eseguibile che cattura le eccezioni di runtime e li gestisce:
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));
Perché non chiamare ExecutorService#submit()
, ottenere il Future
indietro e quindi gestire possibili eccezioni se stessi quando si chiama Future#get()
?
skaffman è corretta in quanto utilizzando submit
è l'approccio più pulito. Un approccio alternativo è quello di creare una sottoclasse ThreadPoolExecutor
e sovrascrivere afterExecute(Runnable, Throwable)
. Se si segue questo approccio assicurarsi di chiamare execute(Runnable)
, piuttosto che non saranno invocate submit(Runnable)
o afterExecute
.
Per la descrizione API:
Metodo invocato al termine del esecuzione della proposta praticabile. Questo metodo viene richiamato dal filo che eseguito il compito. Se non nullo, la Throwable è il non rilevata
RuntimeException
oError
che causato esecuzione di interrompere bruscamente.Nota: Quando le azioni sono racchiuse in attività (come ad esempio FutureTask) sia esplicitamente o attraverso metodi come sostengono, questi oggetti dell'attività di cattura e mantenere eccezioni computazionali, e in modo da non causare brusca terminazione, e l'interno eccezioni sono non passati a questa Metodo .
un compito (Callable
o Runnable
) sottoposti a ThreadPoolExecutors
sarà convertire in FuturnTask
, contiene un puntello di nome callable
è uguale al compito si presenta. FuturnTask ha il proprio metodo di run
come segue. Tutti eccezione o Throwable throwed in c.call()
saranno pescati e messi in un puntello di nome outcome
. Quando si chiama il metodo di get
FuturnTask, outcome
verrà gettato
Da FuturnTask.run Jdk1.8 Codice Sorgente
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);
}
}
...
}
se si vuole intercettare l'eccezione:
- risposta 1. di skaffman
- 2. sovrascrittura `afterExecute` quando si nuovo 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
}
}