11

我想处理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实例吗?

4

2 回答 2

11

这实际上是预期的行为。

引用afterExecuteJavadoc:

如果非 null,则 Throwable 是导致执行突然终止的未捕获的 RuntimeException 或 Error。

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

这里还有其他事情(仍然引用Javadoc):

注意:FutureTask当操作被显式或通过诸如提交等方法包含在任务中时(例如),这些任务对象捕获并维护计算异常,因此它们不会导致突然终止,并且内部异常不会传递给该方法。

在您的示例中,任务包含在 a 中,FutureTask因为您提交的是 a Callable,所以您在这种情况下。即使您更改代码以抛出RuntimeException, if 也不会被给予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);
}
于 2015-11-17T13:56:51.393 回答
2

这是另一种方法。从这里得到提示

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 

反过来失败并突然终止。

于 2015-11-17T14:17:30.407 回答