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 (二维码自动识别)

posted @ 2025-06-05 22:17  奋斗终生  Views(36)  Comments(0)    收藏  举报