为什么 ThreadPoolExecutor 的 afterExecute() 异常为 null?

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

我想处理 ThreadPoolExecutor#afterExecute() 方法中工作线程抛出的异常。目前我有这个代码:

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

如果我 运行 我得到输出的代码:

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

为什么参数Throwable t nullafterExecute()?不应该是 SQLException 实例吗?

这实际上是预期的行为。

引用 afterExecute Javadoc:

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

这意味着 throwable 实例将 RuntimeExceptionError 检查 Exception。由于 SQLException 是已检查异常,因此不会传递给 afterExecute.

这里还有其他事情(仍然引用 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.

在您的示例中,任务包含在 FutureTask 中,因为您提交的是 Callable,所以您属于这种情况。即使在您更改代码以抛出 RuntimeException 时,也不会抛出 afterExecute。 Javadoc 给出了一个示例代码来处理这个问题,我将其复制在这里,以供参考:

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

这是另一种方法。从 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..");
        }
    }
}

afterExecute 的用途不同。

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 

依次失败并突然终止。