Java线程池的execute和submit方法的区别(转)
原文:https://zhuanlan.zhihu.com/p/1894001201957278410
作者:软件求生
来源:知乎
来自社招面试的灵魂拷问
时间回到去年这个时候,我还在准备从原公司跳槽的社招,面了一家国内TOP 10的互联网公司(名字保密哈~)。
面试官突然问我:
“你知道线程池中 submit() 和 execute() 的区别吗?”
我嘴角一笑,心想:“这不是基础题吗?”
“submit 可以拿返回值,execute 不可以!”我自信作答。
面试官点点头,然后轻描淡写地说了一句:
“你说得没错,那你能说说它们底层是怎么实现的吗?还有,它们对异常的处理方式一样吗?”
我:???
基础回答,90%的候选人都停在这里
如果你在面试中遇到这个问题,大多数人(包括我当初)都会这样答:
- execute() 是 Executor 接口中定义的方法,只能执行 Runnable 任务,没有返回值;
- submit() 是 ExecutorService 接口新增的方法,可以执行 Runnable 或 Callable 任务,并返回一个 Future 对象。
没错,这个回答正确。
但也仅限于“正确”,离“惊艳”还有点距离。
如果你能再补充下面这些点,那才叫“把握住了整场面试的节奏”。
面试官真正想听的,是这几个“深水区”问题
我们逐个拆解一下,submit() 和 execute() 真正的区别在哪。
1. 本质区别:返回值 vs 无返回值?
我们先从最直观的感受说起:
- submit() 会返回一个 Future 对象,允许你通过 future.get() 获取任务执行结果。
- 而 execute() 是个“哑巴”——它负责“干活”,但从不“反馈”。
这意味着:
- submit() 更适合需要获取返回值、检测执行结果、处理异常的任务;
- execute() 更适合只管执行,不关心结果的 fire-and-forget 场景。
这部分很多文章都有讲,我们不多说。
接下来进入真正的“深水区”:
2. 异常处理行为大不同(这点90%的人没搞清)
先来看一段代码:
问题:这两段代码执行后,控制台会打印异常栈吗?
答案是:
- execute() 提交的任务,如果抛出未捕获异常,会在控制台打印堆栈信息。
- submit() 提交的任务,即使抛出异常,也不会在控制台打印任何东西!
为什么?因为 submit() 的任务是被封装进了 FutureTask 中,异常也“吞”进去了,只有你主动调用 future.get() 才会抛出异常。
这就意味着:
- submit() 很“安静”,但异常可能悄悄把你的线程搞挂了,还不告诉你;
- execute() 很“直接”,出事了就爆出来,调试起来反而容易。
面试高阶回答点:
- submit() 会吞掉异常,除非主动通过 Future.get() 触发,否则可能导致异常“沉默”;这在业务代码中是个常见“坑”。
一起追源码,看清“内幕”
execute() 源码实现
这是 Executor 接口的方法,ExecutorService 继承了它,直接使用。
submit() 源码实现(以 Runnable 为例)
你看出来了吗?
- submit() 的本质,其实也是调用了 execute(),不过是把任务封装成了 FutureTask 再执行!
- 也就是说,submit() 是 execute() 的一个增强版,提供了任务跟踪能力(Future)、异常封装、返回值机制。
真实开发中如何选择?
我们来看几个业务场景:
场景一:任务只做一些异步日志记录、埋点上传,失败了也无所谓
- 用 execute() 最合适,简单直接,高性能,无需关心结果。
场景二:你需要知道任务是否执行成功
- 用 submit(),然后通过 Future.get() 获取返回值或者异常。
比如:
场景三:批量提交多个任务,并等待所有结果
- 用 submit(),然后通过 invokeAll() 批量获取 Future 列表,统一处理。
面试进阶回答模版(建议收藏)
如果你要答这个题,推荐这样的结构:
“execute() 是 Executor 接口定义的,不能获取返回值;
submit() 是 ExecutorService 扩展的,适合处理需要返回结果的任务。
它本质是将任务封装成 FutureTask,然后调用 execute() 实现执行。
两者最大的区别在于异常处理:
execute() 抛出的异常会直接打印;submit() 会被 FutureTask 吞掉,只有在 get() 时才暴露出来。
所以在业务开发中,如果不调用 get(),可能会造成异常丢失,导致 bug 难以定位。”
如果你能再加上一段源码分析,那就完美了!
小米的踩坑回忆录:我曾经被 submit 的异常“坑惨了”
在我刚入职第二家公司时,有次写了一个批量推送任务,使用线程池的 submit() 提交。
上线后一切正常,结果过了几天突然被客户投诉推送异常了。
查看日志,压根没发现任何异常信息,debug 也没看出来问题。
后来才发现,我的 submit() 后根本没调用 get(),导致里面抛的异常全被吞了!
于是我痛定思痛,写了一套线程池封装工具类,凡是 submit 的任务,都会自动监听 Future 的状态,并在失败时记录日志。顺带,还写了一篇技术博客(哈哈~就是这篇的“前身”)。
总结一下,方便收藏复习
最后的的最后:面试中别忘了这些!
- submit() 的异常处理千万别忘了!一定要 future.get()。
- 说区别时,不要只说“一个有返回值一个没有”,那太基础。
- 最好结合业务场景说“什么时候选哪个”,让面试官觉得你是能落地的人。
- 源码分析一定加分,不要怕说错,大胆讲逻辑。
END
好了,今天的分享就到这里啦~
如果你觉得这篇文章对你有帮助,欢迎点个【赞】【在看】或者【分享给小伙伴】,我会继续更新更多“面试真题 + 实战经验 + 技术故事”的内容!
我们下次见~
我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!
http://weixin.qq.com/r/MxywqEPElh7prQfL90kh (二维码自动识别)