8

Je souhaite gérer les exceptions émises par les threads de travail dans la méthode ThreadPoolExecutor#afterExecute(). À l'heure actuelle, j'ai ce code:Pourquoi l'exception est-elle nulle dans AfterExecute() de 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.."); 
     } 
    } 
} 

Si je lance le code que je reçois la sortie:

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

Pourquoi paramètre Throwable tnull dans afterExecute()? Ne devrait-il pas être l'instance SQLException?

Répondre

7

C'est le comportement attendu.

afterExecute Javadoc Citation:

Si non nul, le Throwable est le uncaught RuntimeException ou erreur qui a provoqué l'exécution de mettre fin brusquement.

Cela signifie l'instance throwable sera RuntimeException ou Error, pas vérifié Exception. Puisque SQLException est une exception vérifiée, il ne sera pas transmis à afterExecute.

Il y a aussi quelque chose d'autre se passe ici (citant toujours le Javadoc):

Note: Lorsque les actions sont placées dans des tâches (telles que FutureTask) soit explicitement ou par des méthodes telles que présenter, ces Les objets de tâche capturent et maintiennent les exceptions de calcul, et ils ne provoquent donc pas de fin brutale, et les exceptions internes ne sont pas transmises à cette méthode.

Dans votre exemple, la tâche est enfermé dans un FutureTask puisque vous soumettez une Callable, donc vous êtes dans ce cas. Même en vous changez votre code pour lancer un RuntimeException, s'il ne sera pas donné à afterExecute. Le Javadoc donne un exemple de code pour faire face à ce que je suis ici la copie, pour référence:

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

Merci d'avoir éclairci cela. Donc toutes les exceptions vérifiées vont juste être avalées par le ThreadPoolExecutor? Et toute la gestion des exceptions doit être effectuée dans Callable # call()? –

+1

@ potato300 Voir mon edit, il y a quelque chose d'autre qui se passe dans votre exemple spécifique (que je n'ai pas remarqué au début) – Tunaki

1

Ceci est une autre façon de le faire. Prendre soupçon de 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'utilisation de afterExecute est un but différent.

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 

échouent à leur tour et se terminent brusquement.