线程池中的一个线程异常了会被怎么处理?
估计很多人会是以下三点答案(me too):
1.抛异常出来并打印在控制台上
2.其他线程任务不受影响
3.异常线程会被回收
但是这里我先提前说一下以上三点不全对,下面我们来具体分析一下。
话不多说用代码来证明
熟悉Executors线程池(本文线程池都是指Executors)都知道 有两种提交线程的方式execute和submit方式,下面将以这两种提交方式来验证。
贴个代码凑个数
public static void main(String[] args) {
ThreadPoolTaskExecutor executorService = buildThreadPoolTaskExecutor();
executorService.execute(() -> run("execute方法"));
executorService.submit(() -> run("submit方法"));
}
private static void run(String name) {
String printStr = "【thread-name:" + Thread.currentThread().getName() + ",执行方式:" + name+"】";
System.out.println(printStr);
throw new RuntimeException(printStr + ",出现异常");
}
private static ThreadPoolTaskExecutor buildThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor executorService = new ThreadPoolTaskExecutor();
executorService.setThreadNamePrefix("(小罗技术笔记)-");
executorService.setCorePoolSize(5);
executorService.setMaxPoolSize(10);
executorService.setQueueCapacity(100);
executorService.setKeepAliveSeconds(10);
executorService.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executorService.initialize();
return executorService;
}
观看执行结果,诶好奇怪
execute执行方式抛出异常显示在控制台了。
submit执行方式啥都没有输出。
众所周知submit底层其实也是调用的execute,因此它也有异常只是处理方法不一样,它们的区别是:
1、execute没有返回值。可以执行任务,但无法判断任务是否成功完成。——实现Runnable接口
2、submit返回一个future。可以用这个future来判断任务是否成功完成。——实现Callable接口
那怎么拿到submit中的异常呢?还是用代码来说话
Future<?> result=executorService.submit(() -> run("submit方法"));
try {
result.get();
}catch (Exception e){
e.printStackTrace();
}
获取了一下submit方法的返回结果,发现有异常了和execute一样了,所以第一点抛异常出来并打印在控制台上不是全对的!
到这估计大家和我一样都有一个疑问了 ,为啥execute直接抛出异常,submit没有呢?
要知道这个答案就只能去翻源码看了
在java.util.concurrent.ThreadPoolExecutor#runWorker中抛出了运行异常:
在java.lang.ThreadGroup#uncaughtException进行了异常处理:
uncaughtException是什么,我也不知道,百度了一下说这个方法是JVM调用的,在线程中只需要指定我们想要的处理方式即可
说道这里你可能会吐槽说了这么多submit到底为啥没有直接抛出异常,到底是怎么处理了,不要慌我们再看源码找答案
看submit源码会发现,submit中传进来的task会被封装成一个FutureTask,然后再调用execute,最后返回FutureTask。
你会发现走的是execute方法,如下图,会发现此时的task已经是FutureTask,所以再去看一下FutureTask的run方法是咋写的。
异常被存起来了....,再看源码是怎么实现的
返回参数里面有一个状态state翻源码发现在report方法中同时用到outcom和state状态判断打个断点发现还真是,当state为3时抛出了异常。
同理在找寻一下report调用位置可以很明显发现是FutureTask中get方法调用了,结合上面可以很明确了submit提交时异常被存储在线程结果信息中,当调用get方法是判断线程运行结果状态,有异常就抛出存储的异常信息,因此submit运行异常我们只能用get方法来拿到。
至于第二点我就不多说了,平时使用中就已经证明了!
第三点线程出异常了不是被线程池回收嘛?
看源码我们知道线程运行最后总有一个processWorkerExit要执行,看看里面的实现
很神奇先删掉线程又再调用创建线程的方法,所以异常线程不是被回收,而是被删除了再创建一个新的顶替了。
到此线程池中的线程异常了会被怎么处理讲完了,总结一下就是:
1、execute方法,可以看异常输出在控制台,而submit在控制台没有直接输出,必须调用Future.get()方法时,可以捕获到异常。
2、一个线程出现异常不会影响线程池里面其他线程的正常执行。
3、线程不是被回收而是线程池把这个线程移除掉,同时创建一个新的线程放到线程池中。
4、还有源码是个好东西,答案都在里面,就是太难看懂了
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。