8

Voglio gestire le eccezioni generate dai thread di lavoro nel metodo ThreadPoolExecutor#afterExecute(). Attualmente ho questo codice:Perché l'eccezione è nullo in AfterExecute() di ThreadPoolExecutor?

public class MyExecutor extends ThreadPoolExecutor { 

    public static void main(String[] args) { 
     MyExecutor threadPool = new MyExecutor(); 
     Task<Object> task = new Task<>(); 
     threadPool.submit(task); 
    } 

    public MyExecutor() { 
     super(4, 20, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(4000)); 
    } 

    @Override 
    protected void afterExecute(Runnable r, Throwable t) { 
     super.afterExecute(r, t); 
     System.out.println("in afterExecute()"); 
     if (t != null) { 
      System.out.println("exception thrown: " + t.getMessage()); 
     } else { 
      System.out.println("t == null"); 
     } 
    } 

    private static class Task<V> implements Callable<V> { 

     @Override 
     public V call() throws Exception { 
      System.out.println("in call()"); 
      throw new SQLException("testing.."); 
     } 
    } 
} 

Se corro il codice ottengo uscita:

in call() 
in afterExecute() 
t == null 

Perché parametro Throwable tnull in afterExecute()? Non dovrebbe essere l'istanza SQLException?

risposta

7

Questo comportamento è in realtà previsto.

Citando afterExecute Javadoc:

Se non nullo, il Throwable è la RuntimeException uncaught o Errore che causava esecuzione per terminare bruscamente.

Ciò significa l'istanza throwable sarà RuntimeException o Error, non controllato Exception. Poiché SQLException è un'eccezione controllata, non verrà passata a afterExecute.

C'è anche qualcos'altro qui (ancora citando il Javadoc):

Nota: Quando le azioni sono racchiusi in compiti (come ad esempio FutureTask) esplicitamente o tramite metodi come presentare, questi gli oggetti task catturano e gestiscono eccezioni di calcolo e pertanto non causano interruzioni improvvise e le eccezioni interne non vengono passate a questo metodo.

Nel tuo esempio, il compito è racchiuso in una FutureTask dal momento che si sta presentando un Callable, in modo che siano in questo caso. Anche in te cambia il tuo codice per lanciare un RuntimeException, se non verrà dato a afterExecute. Il Javadoc dà un codice di esempio per far fronte a questo, che sto copiando qui, per riferimento:

protected void afterExecute(Runnable r, Throwable t) { 
    super.afterExecute(r, t); 
    if (t == null && r instanceof Future) { 
     try { 
     Object result = ((Future) r).get(); 
     } catch (CancellationException ce) { 
      t = ce; 
     } catch (ExecutionException ee) { 
      t = ee.getCause(); 
     } catch (InterruptedException ie) { 
      Thread.currentThread().interrupt(); // ignore/reset 
     } 
    } 
    if (t != null) 
     System.out.println(t); 
} 
+0

Grazie per averlo chiarito. Quindi tutte le eccezioni controllate saranno ingoiate da ThreadPoolExecutor? E tutta la gestione delle eccezioni deve essere eseguita in Callable # call()? –

+1

@ potato300 Vedi la mia modifica, c'è qualcos'altro che sta succedendo nel tuo esempio specifico (che inizialmente non ho notato) – Tunaki

1

Questo è un modo alternativo per farlo. Prendendo spunto da here

package com.autonomy.introspect.service; 

import java.sql.SQLException; 
import java.util.concurrent.*; 

public class MyExecutor extends ThreadPoolExecutor { 

    public static void main(String[] args) { 
     MyExecutor threadPool = new MyExecutor(); 
     Task<Object> task = new Task<Object>(); 
     Future<Object> futureTask = threadPool.submit(task); 
     try { 
      System.out.println(futureTask.get()); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } catch (ExecutionException e) { 
      System.out.println("exception thrown: " + e.getMessage()); 
     } 
    } 

    public MyExecutor() { 
     super(4, 20, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(4000)); 
    } 

    @Override 
    protected void afterExecute(Runnable r, Throwable t) { 
     super.afterExecute(r, t); 
     System.out.println("in afterExecute()"); 
     if (t != null) { 
      System.out.println("exception thrown: " + t.getMessage()); 
     } else { 
      System.out.println("t == null"); 
     } 
    } 

    private static class Task<V> implements Callable<V> { 

     @Override 
     public V call() throws Exception { 
      System.out.println("in call()"); 
      throw new SQLException("testing.."); 
     } 
    } 
} 

L'uso di afterExecute è per uno scopo diverso.

This class provides protected overridable beforeExecute(java.lang.Thread, 
java.lang.Runnable) and afterExecute(java.lang.Runnable, 
java.lang.Throwable) methods that are called before and after execution of 
each task. These can be used to manipulate the execution environment; for 
example, reinitializing ThreadLocals, gathering statistics, or adding log 
entries. Additionally, method terminated() can be overridden to perform any 
special processing that needs to be done once the Executor has fully 
terminated. 

If hook or callback methods throw exceptions, internal worker threads may 

a sua volta non riuscito e terminare bruscamente.