8

Ich möchte Exceptions umgehen, die von Worker-Threads in ThreadPoolExecutor#afterExecute() Methode geworfen werden. Zur Zeit habe ich diesen Code:Warum Ausnahme ist in ThreadPoolExecutor AfterExecute() Null?

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.."); 
     } 
    } 
} 

Wenn ich den Code ausführen I Ausgabe:

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

Warum ist Parameter Throwable tnull in afterExecute()? Sollte es nicht die SQLException Instanz sein?

Antwort

7

Dies ist tatsächlich erwartetes Verhalten.

Zitiert afterExecute Javadoc:

Wenn nicht null, die Throwable ist die abgefangene Runtime oder Fehler, die Ausführung abrupt zu beenden verursacht.

Das bedeutet, die throwable Instanz sein wird RuntimeException oder Error, nichtException geprüft. Da SQLException eine geprüfte Ausnahme ist, wird sie nicht an afterExecute übergeben.

Es gibt auch noch etwas anderes passiert hier (noch das Javadoc zitiert):

Hinweis: Wenn Aktionen werden in Aufgaben (wie FutureTask) entweder explizit oder über Methoden wie einreichen, diese eingeschlossen Aufgabenobjekte fangen und behalten Rechenausnahmen bei und verursachen daher keine abrupte Beendigung, und die internen Ausnahmen werden nicht an diese Methode übergeben.

In Ihrem Beispiel wird die Aufgabe in einem FutureTask eingeschlossen, da Sie ein Callable einreichen, so dass Sie in diesem Fall. Auch in dir ändern Sie Ihren Code, um eine RuntimeException zu werfen, wenn nicht afterExecute gegeben wird. Die Javadoc gibt einen Beispielcode, dies zu beschäftigen, die ich hier bin zu kopieren, als Referenz:

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

Vielen Dank für das Aufräumen. Also werden alle geprüften Ausnahmen einfach vom ThreadPoolExecutor verschluckt? Und alle Ausnahmen müssen in Callable # call() behandelt werden? –

+1

@ potato300 Sehen Sie meine Bearbeitung, es gibt etwas anderes in Ihrem spezifischen Beispiel (das habe ich zuerst nicht bemerkt) – Tunaki

1

Dies ist eine alternative Art und Weise tun. Hinweis von 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.."); 
     } 
    } 
} 

Die Nutzung von AfterExecute zu nehmen ist für einen anderen Zweck.

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 

der Reihe nach ausfallen und abrupt beenden.