为什么java线程池的submit既可以提交runnable也可以提交callable?

举报
经典鸡翅 发表于 2022/02/18 00:43:30 2022/02/18
【摘要】 前言 大家好,今天鸡翅老哥继续带大家深入了解多线程,我们都知道多线程有两种实现方式,一种是实现runnable接口,一种是实现callable接口。那么线程池的submit提交方式,两种都可以作为参数传递。要知道submit是当需要返回值的情况下才使用,runnable是没有返回值,那么submit为什么可以接收两种呢,我们继续往下看...

前言

大家好,今天鸡翅老哥继续带大家深入了解多线程,我们都知道多线程有两种实现方式,一种是实现runnable接口,一种是实现callable接口。那么线程池的submit提交方式,两种都可以作为参数传递。要知道submit是当需要返回值的情况下才使用,runnable是没有返回值,那么submit为什么可以接收两种呢,我们继续往下看。

submit方法解析

先看下submit的方法,来确定,确实可以提交两种方式。

我们定义一个线程池,来执行两个方法,第一种没有返回值,线程池会自动找到runnable接口,第二种有返回值,线程池会走callable接口的方式。这里大家可以直接点击submit进去,就可以看到具体走的哪个方法。


  
  1. @Test
  2. public void test(){
  3. ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,5,5, TimeUnit.SECONDS,new LinkedBlockingDeque<>(1024));
  4. Future<?> submit = threadPoolExecutor.submit(() -> {
  5. System.out.println("submit.runnable.way");
  6. });
  7. Future<String> submit1 = threadPoolExecutor.submit(() -> {
  8. return "submit.callable.way";
  9. });
  10. }

我们从上面可以发现一个现象。无论是runnable还是callable,返回值都是future。callable我们可以理解,他就是返回一个future。那么runnable是没有返回值的,是怎么转换成future的呢。我们继续看

runnable如何转为future的?

先来看第一段源码


  
  1. public Future<?> submit(Runnable var1) {
  2. if (var1 == null) {
  3. throw new NullPointerException();
  4. } else {
  5. RunnableFuture var2 = this.newTaskFor(var1, (Object)null);
  6. this.execute(var2);
  7. return var2;
  8. }
  9. }

我们发现runnable传进去后,通过newTaskFor进行了包装,返回了RunnableFuture,RunnableFuture 又继承了runnable和future。futuretask又实现了这个接口,所以自然就可以返回futuretask了。


  
  1. FutureTask<V> implements RunnableFuture<V>
  2. public interface RunnableFuture<V> extends Runnable, Future<V> {
  3. void run();
  4. }

继续深入到newTaskFor,发现他对runnable进行了包装。


  
  1. protected <T> RunnableFuture<T> newTaskFor(Runnable var1, T var2) {
  2. return new FutureTask(var1, var2);
  3. }

继续跟踪,futuretask的构造参数。发现就是从这一刻开始,你的runnable变为了callable。继续深入到callable方法。


  
  1. public FutureTask(Runnable var1, V var2) {
  2. this.callable = Executors.callable(var1, var2);
  3. this.state = 0;
  4. }
  5. public static <T> Callable<T> callable(Runnable var0, T var1) {
  6. if (var0 == null) {
  7. throw new NullPointerException();
  8. } else {
  9. return new Executors.RunnableAdapter(var0, var1);
  10. }
  11. }

这里面有一个RunnableAdapter的适配器。这里我们可以看到RunnableAdapter实现了Callable。这样返回的时候就是Callable,当我们调用RunnableAdapter的call方法的时候,实际调用的就是runnable的run方法。


  
  1. private static final class RunnableAdapter<T> implements Callable<T> {
  2. private final Runnable task;
  3. private final T result;
  4. RunnableAdapter(Runnable var1, T var2) {
  5. this.task = var1;
  6. this.result = var2;
  7. }
  8. public T call() {
  9. this.task.run();
  10. return this.result;
  11. }
  12. public String toString() {
  13. return super.toString() + "[Wrapped task = " + this.task + "]";
  14. }
  15. }

至此,runnable就转为callable了。

submit提交callable

了解了上面的runnable,再来理解callable就十分简单了。我们看一下他的源码。我们可以看到,直接封装到futuretask。不需要像上面一样进行runnable的转换。


  
  1. public <T> Future<T> submit(Callable<T> var1) {
  2. if (var1 == null) {
  3. throw new NullPointerException();
  4. } else {
  5. RunnableFuture var2 = this.newTaskFor(var1);
  6. this.execute(var2);
  7. return var2;
  8. }
  9. }
  10. protected <T> RunnableFuture<T> newTaskFor(Callable<T> var1) {
  11. return new FutureTask(var1);
  12. }
  13. public FutureTask(Callable<V> var1) {
  14. if (var1 == null) {
  15. throw new NullPointerException();
  16. } else {
  17. this.callable = var1;
  18. this.state = 0;
  19. }
  20. }

结尾

文中难免有不足了,欢迎大家讨论!同时有问题,可以找鸡翅老哥交流哦。

 

文章来源: blog.csdn.net,作者:经典鸡翅,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/hanqing456/article/details/122313490

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。