博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ThreadPoolExecutor中的submit()方法详细讲解
阅读量:3779 次
发布时间:2019-05-22

本文共 7411 字,大约阅读时间需要 24 分钟。

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

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

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

Future
submit(Callable
callable);
Future
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 call() throws Exception;}

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

  • submmit()参数解析

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

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

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

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

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

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

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

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

public static 
Callable
callable(Runnable task, T result) { if (task == null) throw new NullPointerException(); return new RunnableAdapter
(task, result);}private static final class RunnableAdapter
implements Callable
{
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
implements RunnableFuture
{
...}public interface RunnableFuture
extends Runnable, Future
{
void run();}

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

public void run() {        if (state != NEW ||            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))            return;        try {            Callable
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 interrupts int 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 state        finishCompletion();    }}protected void setException(Throwable t) {    if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {        outcome = t;        U.putOrderedInt(this, STATE, EXCEPTIONAL); // final state        finishCompletion();    }}
  • 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
future = executorService.submit(new Callable
() { @Override public 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()调用开始: 1496899867882submit方法执行任务完成: 1496899872897   thread name: pool-1-thread-1经过返回值比较,submit方法执行任务成功    thread name: main-------------------main thread end---------------------

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

转载地址:http://aesvn.baihongyu.com/

你可能感兴趣的文章
VS 2010 测试功能学习(十五) - 用Visual Studio 2010辅助敏捷测试
查看>>
微软发布Visual Studio Scrum 1.0 过程模板
查看>>
VS 2010 测试功能学习(十六) - 十月的MSDN更新
查看>>
VS 2010 测试功能学习(17) – Feature Pack 2 正式发布
查看>>
VS 2010 测试功能学习(18) – Coded UI Test三个必知的函数
查看>>
VS 2010 测试功能学习(19) - 什么情况下应该引入UI自动化测试?
查看>>
VS 2010 测试功能学习(20) - 建立手工测试用例参数和被测试程序控件的绑定
查看>>
设计测试用例的四条原则
查看>>
VS 2010 测试功能学习(21) – Bug 68648 : The Love Bug
查看>>
《Programming Windows Phone 7》- 微软提供的免费WP7编程电子书
查看>>
2011年3月刚出炉的《Professional Team Foundation Server 2010》
查看>>
Visual Studio 2010 Service Pack 1 (SP1)正式版发布了
查看>>
如何清除TFS代码库中不再需要的文件Pending Change和Lock?
查看>>
测试用例Passed和Failed有效性问题
查看>>
Azure Stream Analytics的BadArgument错误
查看>>
Visual Studio 2010之后-Visual Studio vNext
查看>>
代码覆盖从简到繁 (二) – Block Coverage
查看>>
Soma, Jason, ScottGu等的MSDN中文博客
查看>>
代码覆盖从简到繁 (三) – 划分Block
查看>>
Elasticsearch节点磁盘空间耗尽
查看>>