ThreadPoolExecutor中的submit()方法详细讲解

在使用线程池的时候,发现除了execute()方法可以执行任务外,还发现有一个方法submit()可以执行任务。

submit()有3个参数不一的方法,这些方法都是在ExecutorService接口中声明的,在AbstractExecutorService中实现,而ThreadPoolExecutor继承AbstractExecutorService

<T> Future<T> submit(Callable<T> callable);<T> Future<T> submit(Runnable var1, T result);Future<?> submit(Runnable runnable);

我们可以看到submit()的参数既可以是Runnable,又可以是Callable。对于Runnable我们是比较熟的,它是线程Thread所执行的任务,里面有一个run()方法,是任务的具体执行操作。那么Callable呢?我们一起看下他们的代码吧。

public interface Runnable {void run();
}public interface Callable<V> {V call() throws Exception;
}

Runnable这里就不介绍了,Callable接口定义了一个call()方法,返回一个Callable指定的泛型类,并且call()调用的时候会抛出异常。通过比较RunnableCallable还看不什么端倪,那么我们就看看内部实现吧。

  • submmit()参数解析

这里重点分析submit()带参数RunnableCallable的方法

public Future<?> submit(Runnable task) {if (task == null) throw new NullPointerException();RunnableFuture<Void> ftask = newTaskFor(task, null);execute(ftask);return ftask;
}public <T> Future<T> submit(Callable<T> task) {if (task == null) throw new NullPointerException();RunnableFuture<T> ftask = newTaskFor(task);execute(ftask);return ftask;
}

我们发现2者的实现没有任何的差异,唯一就是submit()参数不同。

参数传入newTaskFor()方法,那么可以肯定就是在这个方法里做了什么操作。

protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {return new FutureTask<T>(runnable, value);
}protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {return new FutureTask<T>(callable);
}

newTaskFor()的目的就是创建一个FutureTask对象,那我们追踪到FutureTask的构造方法(FutureTask非常关键,后面会分析)。

public FutureTask(Runnable runnable, V result) {this.callable = Executors.callable(runnable, result);this.state = NEW;
}public FutureTask(Callable<V> callable) {if (callable == null)throw new NullPointerException();this.callable = callable;this.state = NEW;
}

到了这里我们知道,其实Runnable会在这里转化成Callable。我们来看下Executors.callable()具体实现。

public static <T> Callable<T> callable(Runnable task, T result) {if (task == null)throw new NullPointerException();return new RunnableAdapter<T>(task, result);
}private static final class RunnableAdapter<T> implements Callable<T> {private final Runnable task;private final T result;RunnableAdapter(Runnable task, T result) {this.task = task;this.result = result;}public T call() {task.run();return result;}
}

Executors.callable()创建了一个RunnableAdapter对象,RunnableAdapter实现了Callable接口,在call()方法中调用了传入的Runnablerun(),并且将传入的result参数返回。

也就是说我们调用submit()传入的Runnbale最终会转化成Callable,并且返回一个result值(如果我们传入这个参数则返回这个参数,不传入则返回null)。

到这里我们讲清楚了submit()的参数的区别和内部实现,submit()方法有一个返回值Future,下面我们来分析一下返回值Future

  • submit()的返回值Future

上面分析submit()源码可知,submit()返回的是一个RunnableFuture类对象,真正是通过newTaskFor()方法返回一个new FutureTask()对象。所以submit()返回的真正的对象是FutureTask对象。

那么FutureTask是什么,我们来看下它的类继承关系。

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

通过继承关系我们可以明确的知道其实FutureTask就是一个Runnable。并且有自己run()实现。我们来看下FutureTaskrun()是如何实现的。

public void run() {if (state != NEW ||!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))return;try {Callable<V> c = callable;if (c != null && state == NEW) {V result;boolean ran;try {result = c.call();ran = true;} catch (Throwable ex) {result = null;ran = false;setException(ex);}if (ran)set(result);}} finally {// runner must be non-null until state is settled to// prevent concurrent calls to run()runner = null;// state must be re-read after nulling runner to prevent// leaked interruptsint s = state;if (s >= INTERRUPTING)handlePossibleCancellationInterrupt(s);}}

我们在new FutureTask()对象的时候,在FutureTask构造方法中会对state状态赋值为NEW,并且传入一个callable对象。通过FutureTaskrun()我们可以知道,其实就通过state状态判断,调用callable的call()。(如果传入的参数是RunnableRunnableRunnableAdapter类中转化时,在call()中,其实调用的就是Runnablerun()方法)。

所以在submit()方法中,调用了一个execute(task)的方法,实际执行的是FutureTaskrun(),而FutureTaskrun()调用的是Callablecall()方法。

说了这么多,submit()最后执行的还是传入的Runnablerun()Callablecall()方法。好像没有FutureTask什么事啊。

其实不是,submit()返回FutureTask对象,通过这个FutureTask对象调用get()可以返回submit()方法传入的一个泛型类参数result对象,如果是Callable直接通过call()返回。这个返回值的可以用来校验任务执行是否成功。

  • FutureTask的get()的实现
public V get() throws InterruptedException, ExecutionException {int s = state;if (s <= COMPLETING)s = awaitDone(false, 0L);   //等待任务执行完return report(s);//将执行的任务结果返回
}private V report(int s) throws ExecutionException {Object x = outcome;if (s == NORMAL)return (V)x;if (s >= CANCELLED)throw new CancellationException();throw new ExecutionException((Throwable)x);
}

最后是通过outcome参数将根据任务的状态将结果返回。那么outcome参数在哪里赋值了?outcome参数赋值的地方有好2处,一是FutureTaskset(),二是FutureTasksetException()

set()是在FutureTaskrun()执行完成后,将传入的result参数赋值给传入给set(),赋值给outcome参数。如果run()报异常了会将Throwable对象通过setException()方法传入,赋值给outcome变量

大家可以返回上面的run()查看下。

protected void set(V v) {if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {outcome = v;U.putOrderedInt(this, STATE, NORMAL); // final statefinishCompletion();}
}protected void setException(Throwable t) {if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {outcome = t;U.putOrderedInt(this, STATE, EXCEPTIONAL); // final statefinishCompletion();}
}
  • submit()使用案例
public class Test {private static final String SUCCESS = "success";public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(3);System.out.println("------------------任务开始执行---------------------");Future<String> future = executorService.submit(new Callable<String>() {@Overridepublic String call() throws Exception {Thread.sleep(5000);System.out.println("submit方法执行任务完成" + "   thread name: " + Thread.currentThread().getName());return SUCCESS;}});try {String s = future.get();if (SUCCESS.equals(s)) {String name = Thread.currentThread().getName();System.out.println("经过返回值比较,submit方法执行任务成功    thread name: " + name);}} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}System.out.println("-------------------main thread end---------------------");}
}

打印结果:

------------------任务开始执行---------------------
call()调用开始: 1496899867882
submit方法执行任务完成: 1496899872897   thread name: pool-1-thread-1
经过返回值比较,submit方法执行任务成功    thread name: main
-------------------main thread end---------------------

主线程会一直阻塞,等待线程池中的任务执行完后,在执行后面的语句。

ThreadPoolExecutor中的submit()方法详细讲解相关推荐

  1. javascript中常用数组方法详细讲解

    javascript中数组常用方法总结 1.join()方法: Array.join()方法将数组中所以元素都转化为字符串链接在一起,返回最后生成的字符串.也可以指定可选的字符串在生成的字符串中来分隔 ...

  2. python中的ix是啥_pandas中ix的使用详细讲解

    在上一篇博客中,我们已经仔细讲解了iloc和loc,只是简单了提到了ix.这是因为相比于前2者,ix更复杂,也更让人迷惑. 因此,本篇博客通过例子的解释试图来描述清楚ix,尤其是与iloc和loc的联 ...

  3. Require使用方法详细讲解

    Require使用方法详细讲解 文章目录 Require使用方法详细讲解 一.AMD 规范 1,AMD 基本介绍 2,AMD 模块规范 二.RequireJS 介绍 1,什么是 RequireJS 2 ...

  4. python中的object是什么意思_Python object类中的特殊方法代码讲解

    python版本:3.8class object: """ The most base type """ # del obj.xxx或del ...

  5. java中PreparedStatement和Statement详细讲解

    java中PreparedStatement和Statement详细讲解 大家都知道PreparedStatement对象可以防止sql注入,而Statement不能防止sql注入,那么大家知道为什么 ...

  6. String类中方法详细讲解

    学习目标: String类方法详细讲解 学习内容: 1.value属性 了解String类的value属性: private final char value[] ; //String类的不可变特性就 ...

  7. Python海龟Turtle的使用画中秋画的方法详细讲解

    最近发现很多博主使用Turtle库进行画作,今天来详细讲解海龟库方法 一.定义: Python的turtle库是一个用于绘制图形的库,它来自 Wally Feurzeig, Seymour Paper ...

  8. 队列使用方法详细讲解

    队列详细讲解 队列是一种先进先出的线性表,它只允许在表的一段进行插入元素,在表的另一端删除元素,先进先出,插入的一端叫做队尾(back),删除的一段叫做对头(front) 队列定义在<queun ...

  9. 慎用jQuery中的submit()方法

    今天在做项目的过程中用到了submit()提交表单. 折腾许久很是郁闷,经过多方资料查询和亲测后,得出结论: 一定要慎用submit()方法 首先,在form表单中一定不要将input中的name或i ...

  10. Java中split方法详细讲解

    1.split()方法介绍 语法: public String split(String regex) public String split(String regex, int limit) 参数: ...

最新文章

  1. nginx实现web负载均衡
  2. 1044 拦截导弹——http://codevs.cn/problem/1044/
  3. SAP中程序间的相互调用,SUBMIT关键字的用法
  4. CPU和内存之间——地址映射(理解很重要)
  5. CSS基础(part1)--引入CSS的方式
  6. egg --- 配置连接mysql 创建模型 插入数据
  7. javascipt -- find方法和findIndex方法的实现
  8. HDU 2504 又见GCD
  9. Java程序员高效开发必备工具,其中有你的最爱吗?
  10. 安卓应用,在吾手机上正常,在另外手机上崩溃,因为缺少so库
  11. MAC编译OpenJDK8:error: ‘‘ within ‘||‘ [-Werror,-Wlogical-op-parentheses]
  12. OpenLayers 6 实现仿Echarts风格的动态迁徙图/航班图
  13. Linux系统中常用的shell命令(文件目录操作)
  14. java超链接大全_JavaFX超链接
  15. Redis集群单点故障:corrupted cluster config file.
  16. python白鹅类型_SCP-CN-161 翻译器
  17. 一个基于场景感应的android智能手机防丢失系
  18. vs2017无法打开文件atls.lib问题
  19. Nordic新发布nRF21540与RFX2401C和CC2592性能对比
  20. 2019年6月1日第十四周UML项目日志

热门文章

  1. 工作之余的抓包乐趣,fiddler抓包、Wireshark抓包
  2. 制作一个html网页的步骤,制作一个完整的网页的步骤
  3. 中企动力助力亚美日化 打造互联网一站式综合服务平台
  4. linux shell脚本 cp,shell命令之cp
  5. 如何将产品发布到App Store上?
  6. Latex 页眉页脚在一章的第1页之后不显示及位置混乱的问题解决
  7. 计算机蓝牙传输,怎么使用电脑蓝牙 电脑蓝牙传输文件步骤【步骤教程】
  8. 电脑 蓝牙不显示 服务器,电脑蓝牙无法使用桌面右下角蓝牙图标不见的解决方法...
  9. 计算机就业去哪个岗位好? 算法岗位还是开发岗位更好?
  10. 报错:UnicodeDecodeError: ‘gbk‘ codec can‘t decode byte 0x93 in position 1721: illegal multibyte sequen