-
execute 只能执行 Runnable 类型的任务。 -
submit 除了可以执行 Runnable 类型的任务外还可以执行 Callable 类型的任务 。
-
execute 没有返回值。 -
submit 返回异步执行结果Future。
-
execute 对异常不做任何处理,会直接在 执行任务的线程 中打印出来。注意不是主线程。
-
submit 对异常进行了封装,报错不会直接抛出,只有调用 get 方法获取结果时才会抛出异常。且这个异常是可以在主线程中捕获的。
首先我们来看下线程池 ThreadPoolExecutor 的依赖关系图。
由图可见 ThreadPoolExecutor 继承自抽象类AbstractExecutorService。
从而实现了接口 Executor 和 ExecutorService。
我们分别看下接口 Executor 和 ExecutorService的源码。
java.util.concurrent.Executor
java.util.concurrent.ExecutorService
从源码上可以很直观的看到前两个不同点,可执行任务类型和返回值不同。
我们再点到实现类里面瞅瞅。
源码中可以看到 execute 方法是在 ThreadPoolExecutor 中实现的,主要作用就是调用线程池中的线程执行给定任务。
java.util.concurrent.ThreadPoolExecutor
一路向下走,最终执行任务的代码模块是 runWorker 方法。
看下图红框标注:
1. 执行任务,
2. 向外抛出异常。
到此我们知道 execute 方法执行任务时会向外抛出异常。
接着继续研究 submit ,submit 是在 AbstractExecutorService 抽象类中实现的。每个实现方法中都有两个步骤,分别是:
1. 将 Runnable 或者 Callable 任务封装成一个 FutureTask 类任务;
2. 调用 execute 方法执行 FutureTask。
因为 AbstractExecutorService 并没有实现 execute 方法,所以此处调用的是我们上面提到的其子类 ThreadPoolExecutor 的 execute 方法。
java.util.concurrent.AbstractExecutorService
看到这里你可能会问了,既然submit最终还是调用了execute方法,为什么他们对异常的处理机制不一样呢?
不急,我们继续往下撸源码。
线程池执行任务时,最终会调用任务本身的run方法,如下所示。
上面提到 submit 会将任务封装成FutureTask,也就是说线程池线程执行任务时首先执行的是封装后的 FutureTask 的 run 方法。
来,我们看看 FutureTask 的 run 方法在出现异常后做了啥?
调用了 setException 方法。
java.util.concurrent.FutureTask
继续往下走, setException 方法将 FutureTask 任务状态设置为异常 (EXCEPTIONAL)。
再然后呢?然后就没有然后啦。
因为FutureTask 的 run 方法把异常catch掉后并没有向外抛,而是将FutureTask改为异常状态,所以最终导致execute方法并不会捕获到异常。
而真正能获得异常的地方是 FutureTask 的 get 方法,即获取异步任务执行结果的时候。
当FutureTask状态异常的时候,抛出ExecutionExcepiton。
因为 get 方法是在主线程中调用的,所以主线程可以捕获到这个异常。
我们写个栗子瞅瞅,直接向submit提交的任务中搞个错误。
可以看到异常是在主线程 main 中打印出来的。
OK,通过源码分析,我们就可以非常清楚明白的知道 execute 和 submit 的区别啦。
另外需要注意的一点是:如果使用了 submit 提交任务,一定要记得使用 get 方法获取结果,不管你需不需要这个结果,不然任务报错了你都不知道。不过话说回来,既然不需要结果就不要用 submit,直接用 execute 就好了。
点个
原文始发于微信公众号(i余数):JDK源码分析:线程池 submit 和 execute 有啥区别?
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/194083.html