Come intercettare le eccezioni in FutureTask
-
30-09-2019 - |
Domanda
Dopo aver constatato che FutureTask
in esecuzione in un Executors.newCachedThreadPool()
su Java 1.6 (e da Eclipse) inghiotte eccezioni nel metodo Runnable.run()
, ho cercato di trovare un modo per catturare questi senza l'aggiunta di tiro / catch a tutti i miei implementazioni Runnable
.
L'API suggerisce che l'override FutureTask.setException()
dovrebbe aiutare in questo:
Provoca questo futuro per segnalare un ExecutionException con la throwable data come sua causa, a meno che questo futuro è già stata impostata o è stato annullato. Questo metodo viene richiamato internamente dal metodo run in caso di guasto del calcolo.
Tuttavia questo metodo non sembra di essere chiamato (corsa con gli spettacoli debugger l'eccezione viene catturata da FutureTask
, ma setException
non è chiamato). Ho scritto il seguente programma per riprodurre il mio 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);
}
}
La mia domanda principale è: come posso intercettare le eccezioni gettati in un FutureTask? Perché non setException
ottenere chiamato?
Inoltre vorrei sapere perché il meccanismo Thread.UncaughtExceptionHandler
non viene utilizzato da FutureTask
, v'è alcuna ragione per questo?
Soluzione
setException
probabilmente non è fatta per superiori, ma viene fornito per consentire di impostare il risultato di un'eccezione, in caso di necessità. Che cosa si vuole fare è l'override del metodo done()
e cercare di ottenere il risultato:
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);
}
}
}
Altri suggerimenti
Hai provato a usare un UncaughtExceptionHandler
?
- È necessario implementare il
UncaughtExceptionHandler
interfaccia. - Per impostare un
UncaughtExceptionHandler
per i thread piscina, fornire unThreadFactory
nella chiamataExecutor.newCachedThreadPool(ThreadFactory)
. - È possibile impostare l'UncaughtExceptionHandler per il thread creato tramite
setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
Invia i compiti con ExecutorService.execute
, perché solo eccezioni generate dalle attività presentate con execute
rendono al gestore di eccezioni non rilevate. Per le attività presentate con ExecutorService.submit
qualsiasi eccezione generata è considerato come parte del valore di ritorno del compito. Se un'attività presentata con presentare termina con un'eccezione, è rilanciati quando si chiama Future.get
, avvolto in un ExecutionException
Una soluzione molto migliore: Java FutureTask controllo completamento
Quando si chiama futureTask.get()
per recuperare il risultato del calcolo sarà un'eccezione (ExecutionException) se il sottostante Runnable
/ Callable
ha generato un'eccezione.
ExecutionException.getCause()
restituirà l'eccezione che il Runnable
/ Callable
gettato.
Sarà anche un'eccezione diversa se il Runnable
/ Callable
è stata annullata.
Ho guardato il codice sorgente di FutureTask
e non riusciva a trovare dove setException
viene chiamato.
V'è un metodo innerSetException
da FutureTask.Sync
(classe interna di FutureTask
) che viene chiamato in caso di Throwable
essendo generata dal metodo run. Questo metodo è anche chiamato in setException
.
Così cuciture come il javadoc non è corretto (o molto difficile da capire ...).
Ci sono tre modi standard e un modo improvvisato. 1. utilizzo UncaughtExceptionHandler, impostare l'UncaughtExceptionHandler per il filo creato il
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable ex) {..}}
* Ma la limitazione è che cattura l'eccezione generata dal thread, ma in caso di operazione futuro, si tratta di ingestione.
2. Uso afterExecute
dopo aver effettuato una threadpoolexecutor personalizzato con gancio che è stato fornito appositamente per questo scopo. Guardando attraverso il codice di ThreadpoolExecutor, tramite presentare> execute (c'è un WorkQueue, workQueue.offer
), i compiti vengono aggiunti alla coda di lavoro
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**();
..}
-
Poi, il terzo sta usando semplice tentativo di cattura all'interno della chiamata al metodo, ma non si può prendere l'esterno un'eccezione qui.
-
La soluzione sta chiamando tutti i metodi di chiamata da una chiamata al metodo di un TaskFactory, una fabbrica che rilascia callable.