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?

È stato utile?

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 un ThreadFactory nella chiamata Executor.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**();
..}
  1. 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.

  2. La soluzione sta chiamando tutti i metodi di chiamata da una chiamata al metodo di un TaskFactory, una fabbrica che rilascia callable.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top