我'm trying to use Java' s ThreadPoolExecutor
类用固定数量的线程运行大量重量级任务 . 每个任务都有许多地方,在这些地方可能因异常而失败 .
我已经子类化 ThreadPoolExecutor
并且我已经覆盖了 afterExecute
方法,该方法应该提供运行任务时遇到的任何未捕获的异常 . 但是,我似乎无法使其发挥作用 .
例如:
public class ThreadPoolErrors extends ThreadPoolExecutor {
public ThreadPoolErrors() {
super( 1, // core threads
1, // max threads
1, // timeout
TimeUnit.MINUTES, // timeout units
new LinkedBlockingQueue<Runnable>() // work queue
);
}
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if(t != null) {
System.out.println("Got an error: " + t);
} else {
System.out.println("Everything's fine--situation normal!");
}
}
public static void main( String [] args) {
ThreadPoolErrors threadPool = new ThreadPoolErrors();
threadPool.submit(
new Runnable() {
public void run() {
throw new RuntimeException("Ouch! Got an error.");
}
}
);
threadPool.shutdown();
}
}
这个程序的输出是“一切都很好 - 情况正常!”即使提交给线程池的唯一Runnable也会引发异常 . 有什么线索在这里发生了什么?
谢谢!
11 回答
我正在使用jcabi-log中的VerboseRunnable类,它吞下所有异常并记录它们 . 非常方便,例如:
来自docs:
当你提交一个Runnable时,它将被包含在Future中 .
你的afterExecute应该是这样的:
WARNING :应该注意,此解决方案将阻止调用线程 .
如果要处理任务抛出的异常,则通常最好使用
Callable
而不是Runnable
.允许
Callable.call()
抛出已检查的异常,并将这些异常传播回调用线程:如果
Callable.call()
抛出异常,则会将其包装在ExecutionException
中并由Future.get()
抛出 .这可能比继承
ThreadPoolExecutor
更为可取 . 如果异常是可恢复的,它还为您提供重新提交任务的机会 .这种行为的解释正好在_1692168中:
我通过将提交的runnable包装提交给执行程序来解决它 .
另一种解决方案是使用ManagedTask和ManagedTaskListener .
您需要一个Callable或Runnable来实现ManagedTask接口 .
方法
getManagedTaskListener
返回所需的实例 .并在ManagedTaskListener中实现
taskDone
方法:有关managed task lifecycle and listener的更多详细信息 .
如果要监视任务的执行,可以旋转1或2个线程(可能更多,具体取决于负载)并使用它们从ExecutionCompletionService包装器中获取任务 .
如果您的
ExecutorService
来自外部源(即,无法继承ThreadPoolExecutor
并覆盖afterExecute()
),则可以使用动态代理来实现所需的行为:这是因为
AbstractExecutorService :: submit
将runnable
包装成RunnableFuture
(只有FutureTask
),如下所示然后
execute
会将其传递给Worker
,Worker.run()
会调用以下内容 .这有效
它派生自SingleThreadExecutor,但您可以轻松地进行调整
Java 8 lamdas代码,但易于修复
它将创建一个具有单个线程的Executor,可以完成很多任务;并将等待当前的一个结束执行以从下一个开始
如果出现unaugth错误或异常,则uncaughtExceptionHandler将捕获它
而不是继承ThreadPoolExecutor,我会提供一个创建新线程的ThreadFactory实例并为它们提供UncaughtExceptionHandler