多线程中抛异常的这个面试题你会吗?



一道面试题

本文相当于是对上面这篇文章的一个补充。

当执行方式是 submit 方法时,在控制台不会打印堆栈异常:

那么怎么获取这个 submit 方法提交时的异常信息呢?

具体原因,我在之前的文章里面详细分析过,就不赘述了,直接看结论:

然后一个读者找我聊天,说为什么他这样写,通过 future.get 方法没有抛出异常呢,和我文章里面说的不一样呢?
public?class?ExecutorsTest?{
????public?static?void?main(String[]?args)?{
????????ThreadPoolExecutor?executorService?=?new?ThreadPoolExecutor(2,?2,
????????????????30,?TimeUnit.SECONDS,?new?ArrayBlockingQueue<>(10));
????????Future?future?=?executorService.submit(()?->?{
????????????try?{
????????????????sayHi("submit");
????????????}?catch?(Exception?e)?{
????????????????System.out.println("sayHi?Exception");
????????????????e.printStackTrace();
????????????}
????????});
????????try?{
????????????future.get();
????????}?catch?(Exception?e)?{
????????????System.out.println("future.get?Exception");
????????????e.printStackTrace();
????????}
????}
????private?static?void?sayHi(String?name)?throws?RuntimeException?{
????????String?printStr?=?"【thread-name:"?+?Thread.currentThread().getName()?+?",执行方式:"?+?name?+?"】";
????????System.out.println(printStr);
????????throw?new?RuntimeException(printStr?+?",我异常啦!哈哈哈!");
????}
}

我寻思这没毛病呀,这不是很正常吗?不就是应该这样输出吗?

过了一小会,他应该是实验完了,又找过来了。

寻找答案-FUTURE
子线程中捕获了异常,则调用返回的 future 的 get 方法,不会抛出异常。 子线程中没有捕获异常,则调用返回的 future 的 get 方法,会抛出异常。

而这个接口有非常多的实现类。我们找哪个实现类呢?
就是下面这个实现类:

get 方法的逻辑很简单,首先判断当前状态是否已完成,如果不是,则进入等待,如果是,则进入 report 方法。

在 FutureTask ?里面,一共有 7 种状态。这 7 种状态之间的流转关系已经在注释里面写清楚了。

所以,一目了然,一个任务的终态有四种:NORMAL、EXCEPTIONAL、CANCELLED、INTERRUPTED。

如果当前状态是小于 COMPLEING 的。

记住这几种状态,然后看这个 report 方法:

这个方法是干啥的?

而这个“剩下的情况”是什么情况?

那么到底是不是这样的呢?

寻找答案-线程池

submit 方法提交的时候把任务包裹了一层,就是用 FutureTask 包裹的:

可以看到,FutureTask 的构造方法里面默认了状态为 NEW。

这个 task 是一个 FutureTask,所以 run 方法其实是 FutureTask 的 run 方法。

答案都藏在这个方法里面。

首先,两个方法都是先进行一个 cas 的操作,把当前 FutureTask 的 status 字段从 NEW 修改为 COMPLETING 。

注意这里,如果 cas 操作失败了,则不会进行任何操作。

而到底是调用 setException 方法还是 set 方法,取决于标号为 ① 的地方是否会抛出异常。

而通过 submit 方法提交任务时写法分别如下:

线程池拒绝异常

那么对于这个异常,如果我们不进行捕获,是不是也不会打印呢?

我们定义的这个线程池最大容量是 7 个任务。

抛出了一次异常,执行完成了 7 个任务。

而这个方法里面,会输出错误堆栈:

所以,当我们没有捕获异常的时候,会在这里打印一次堆栈日志。

再次运行:

10 个任务,三次异常,完成了 7 个任务。

终极答案

第二种情况,如果不捕获异常,会触发 dispatchUncaughtException 方法,因为 runWorker 方法的源码里面虽然捕获了异常,但是又抛出去了:

而我们自己没有捕获,所以会触发 dispatchUncaughtException 方法。

你肯定知道这是会抛出异常的吧。

我们完全没有打印日志的代码吧?

是不是平时根本就没有注意这个点。

写这篇文章的时候我又问了她近况,她已经不在乎这些技术问题了,因为她从程序媛摇身一变,变成了产品汪。

才疏学浅,难免会有纰漏,如果你发现了错误的地方,可以在留言区提出来,我对其加以修改。


更多精彩推荐
?国产开源,GitHub 标星 47000+ ,百度飞桨从打响第一枪到战役突围
?硅谷2020最新大数据学习路线:科学使用这一招,12周助你成为数据分析师
?国产开源,GitHub 标星 47000+ ,百度飞桨从打响第一枪到战役突围
?用以太坊承载的比特币,还只是起步阶段
点分享 点点赞 点在看
关注公众号:拾黑(shiheibook)了解更多
[广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
关注网络尖刀微信公众号随时掌握互联网精彩
- 1 习近平将发表二〇二六年新年贺词 7904141
- 2 2026年国补政策来了 7808738
- 3 东部战区:开火!开火!全部命中! 7712893
- 4 2026年这些民生政策将惠及百姓 7616985
- 5 小学食堂米线过期2.5小时被罚5万 7519709
- 6 解放军喊话驱离台军 原声曝光 7428214
- 7 为博流量直播踩烈士陵墓?绝不姑息 7327605
- 8 每月最高800元!多地发放养老消费券 7238391
- 9 数字人民币升级 1月1日起将计付利息 7141831
- 10 2026年1月1日起 一批新规将施行 7040675











CSDN
