前言
ThreadPoolExecutor通过execute方法提交任务,任务执行过程中出现异常,会导致线程退出,异常信息即堆栈由标准错误(System.err)输出。
ThreadPoolExecutor通过submit方法提交任务,任务执行过程中出现异常,线程不会退出,但是异常会吞掉,并且异常会设置到
java.util.concurrent.FutureTask 中返回。
源码解析:ThreadPoolExecutor通过execute方法提交任务,任务执行过程中出现异常,会导致线程退出,异常信息即堆栈由标准错误(System.err)输出
通过
java.util.concurrent.ThreadPoolExecutor#execute
方法提交任务,任务执行由:
java.util.concurrent.ThreadPoolExecutor#runWorker
执行,出现异常时会重新把异常抛出:
如果提交的任务代码没有处理异常,则有线程本身来处理:
java.lang.Thread#dispatchUncaughtException
/**
* Dispatch an uncaught exception to the handler. This method is
* intended to be called only by the JVM.
*/
private void dispatchUncaughtException(Throwable e) {
getUncaughtExceptionHandler().uncaughtException(this, e);
}
/**
* Returns the handler invoked when this thread abruptly terminates
* due to an uncaught exception. If this thread has not had an
* uncaught exception handler explicitly set then this thread's
* {@code ThreadGroup} object is returned, unless this thread
* has terminated, in which case {@code null} is returned.
* @since 1.5
* @return the uncaught exception handler for this thread
*/
public UncaughtExceptionHandler getUncaughtExceptionHandler() {
return uncaughtExceptionHandler != null ?
uncaughtExceptionHandler : group;
}
如果当前线程没有设置UncaughtExceptionHandler,则由该线程所属的线程组ThreadGroup来处理,异常信息堆栈最终由标准错误输出::
java.lang.ThreadGroup#uncaughtException
异常处理完回到任务处理流程,导致while循环从队列中取任务处理中断即线程退出,执行finally 块:
java.util.concurrent.ThreadPoolExecutor#runWorker
为即将死去的worer执行清理工作:
这种行为有什么缺陷呢?一是异常并非由日志系统输出,二是线程会退出,失去了线程池的意义。
如何避免呢?我们需要在提交的任务中自行处理异常,不再抛出此异常,并且日志输出异常堆栈,最好设置线程的UncaughtExceptionHandler 作为异常的输出兜底处理:
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("test-pool-0")
.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
log.error("uncaughtException {}",t, e);
}
})
.build();
源码解析:ThreadPoolExecutor通过submit方法提交任务,任务执行过程中出现异常,线程不会退出,但是异常会吞掉,并且异常会设置到
java.util.concurrent.FutureTask 中返回
通过
java.util.concurrent.AbstractExecutorService#submit
方法提交任务,原任务被封装为
java.util.concurrent.FutureTask
以便于处理任务执行的结果。
任务执行时:
java.util.concurrent.ThreadPoolExecutor#runWorker
会执行
java.util.concurrent.FutureTask#run
当任务抛出异常时候,FutureTask 会把异常保存,不再继续抛出异常,即吞掉异常:
由于异常被吞掉,job的执行:
java.util.concurrent.ThreadPoolExecutor#runWorker
永远看不到任务的异常,while循环从队列中取任务继续执行,不会退出。
我们想要得知任务有没有异常,也只能通过:
java.util.concurrent.FutureTask#get()
获取,所以,永远不能忽略任务的结果,否则任务执行中发生的异常无从得知。
这种行为有什么缺陷呢?如果我们使用线程池的submit方法提交任务,任务没处理异常,而又不关心结果,即没调用:
java.util.concurrent.FutureTask#get()
,那我们任务的异常,就永远的丢失了。
如何避免呢?很简单,如果我们使用线程池的submit方法提交任务,一定记得调用
java.util.concurrent.FutureTask#get()
,即使我们不关心结果,也必须使用。
小结
ThreadPoolExecutor通过execute方法提交任务,任务执行过程中出现异常,会导致线程退出,异常信息即堆栈由标准错误(System.err)输出。
如何避免呢?我们需要在提交的任务中自行处理异常,不再抛出此异常,并且日志输出异常堆栈,最好设置线程的UncaughtExceptionHandler 作为异常的输出兜底处理。
ThreadPoolExecutor通过submit方法提交任务,任务执行过程中出现异常,线程不会退出,但是异常会吞掉,并且异常会设置到
java.util.concurrent.FutureTask 中返回。
如何避免呢?如果我们使用线程池的submit方法提交任务,一定记得调用
java.util.concurrent.FutureTask#get()
方法。