Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why exception is null in ThreadPoolExecutor's afterExecute()?

I want to handle exeptions thrown by worker threads in ThreadPoolExecutor#afterExecute() method. Currently I have this code:

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

If I run the code I get output:

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

Why is parameter Throwable t null in afterExecute()? Shouldn't it be the SQLException instance?

like image 851
Kaarel Purde Avatar asked Nov 17 '15 13:11

Kaarel Purde


1 Answers

This is actually expected behaviour.

Quoting afterExecute Javadoc:

If non-null, the Throwable is the uncaught RuntimeException or Error that caused execution to terminate abruptly.

This means the throwable instance will be RuntimeException or Error, not checked Exception. Since SQLException is a checked exception, it won't be passed to afterExecute.

There is also something else going on here (still quoting the Javadoc):

Note: When actions are enclosed in tasks (such as FutureTask) either explicitly or via methods such as submit, these task objects catch and maintain computational exceptions, and so they do not cause abrupt termination, and the internal exceptions are not passed to this method.

In your example, the task is enclosed in a FutureTask since you are submitting a Callable, so you are in this case. Even in you change your code to throw a RuntimeException, if won't be given to afterExecute. The Javadoc gives a sample code to deal with this, which I'm copying here, for reference:

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);
}
like image 107
Tunaki Avatar answered Sep 22 '22 01:09

Tunaki