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

目录
  • submmit()参数解析
  • submit()的返回值Future
  • FutureTask的get()的实现
  • 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()调用的时候会抛出异常。通过比较Runnable和Callable还看不什么端倪,那么我们就看看内部实现吧。

submmit()参数解析

这里重点分析submit()带参数Runnable和Callable的方法

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()方法中调用了传入的Runnable的run(),并且将传入的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()实现。我们来看下FutureTask的run()是如何实现的。

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 interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

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

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

说了这么多,submit()最后执行的还是传入的Runnable的run()或Callable的call()方法。好像没有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处,一是FutureTask的set(),二是FutureTask的setException()。

set()是在FutureTask的run()执行完成后,将传入的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<String> future = executorService.submit(new Callable<String>() {
            @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()调用开始: 1496899867882
submit方法执行任务完成: 1496899872897   thread name: pool-1-thread-1
经过返回值比较,submit方法执行任务成功    thread name: main
-------------------main thread end---------------------

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

到此这篇关于ThreadPoolExecutor中的submit()方法详细讲解的文章就介绍到这了,更多相关ThreadPoolExecutor submit()方法内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 简单谈谈ThreadPoolExecutor线程池之submit方法

    jdk1.7.0_79 在上一篇<ThreadPoolExecutor线程池原理及其execute方法>中提到了线程池ThreadPoolExecutor的原理以及它的execute方法.本文解析ThreadPoolExecutor#submit. 对于一个任务的执行有时我们不需要它返回结果,但是有我们需要它的返回执行结果.对于线程来讲,如果不需要它返回结果则实现Runnable,而如果需要执行结果的话则可以实现Callable.在线程池同样execute提供一个不需要返回结果的任务执行,而对

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

    目录 submmit()参数解析 submit()的返回值Future FutureTask的get()的实现 submit()使用案例 在使用线程池的时候,发现除了execute()方法可以执行任务外,还发现有一个方法submit()可以执行任务. submit()有3个参数不一的方法,这些方法都是在ExecutorService接口中声明的,在AbstractExecutorService中实现,而ThreadPoolExecutor继承AbstractExecutorService. <T

  • C++二叉树的前序中序后序非递归实现方法详细讲解

    目录 二叉树的前序遍历 二叉树的中序遍历 二叉树的后序遍历 总结 二叉树的前序遍历 前序遍历的顺序是根.左.右.任何一颗树都可以认为分为左路节点,左路节点的右子树.先访问左路节点,再来访问左路节点的右子树.把访问左路节点的右子树看成一个子问题,就可以完整递归访问了. 先定义栈st存放节点.v存放值,TreeNode* cur,cur初始化为root. 当cur不为空或者栈不为空的时候(一开始栈是空的,cur不为空),循环继续:先把左路节点存放进栈中,同时把值存入v中,一直循环,直到此时的左路节点

  • pandas中ix的使用详细讲解

    在上一篇博客中,我们已经仔细讲解了iloc和loc,只是简单了提到了ix.这是因为相比于前2者,ix更复杂,也更让人迷惑. 因此,本篇博客通过例子的解释试图来描述清楚ix,尤其是与iloc和loc的联系. 首先,再次介绍这三种方法的概述: loc gets rows (or columns) with particular labels from the index. loc从索引中获取具有特定标签的行(或列). iloc gets rows (or columns) at particular

  • java、spring、springboot中整合Redis的详细讲解

    java整合Redis 1.引入依赖或者导入jar包 <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> 2.代码实现 public class JedisTest { public static void main(String[]

  • C++双线程调用网络摄像头与多线程调用多摄像头同步执行方法详细讲解

    目录 一.使用双线程调用网络摄像头并执行算法 方法一 方法二 二.使用多线程调用多路摄像头并同步执行多个算法 在调用网络摄像头处理自己的算法时,当解码的速度与算法运行的速度差太多时,会出现类似下面的错误 error while decoding MB 148 4, bytestream 所以需要使用两个线程,一个线程调用摄像头,一个线程用来处理图像. 一.使用双线程调用网络摄像头并执行算法 方法一 #include <iostream> #include <thread> #inc

  • SpringBoot整合Mybatis与MybatisPlus方法详细讲解

    目录 一.整合MyBatis操作 1.配置模式 2.注解模式 3.混合模式 二.整合 MyBatis-Plus 完成CRUD 1.什么是MyBatis-Plus 2.整合MyBatis-Plus 3.CRUD功能 一.整合MyBatis操作 官网:MyBatis · GitHub SpringBoot官方的Starter:spring-boot-starter-* 第三方的starter的格式: *-spring-boot-starter <dependency> <groupId>

  • Java Feign微服务接口调用方法详细讲解

    目录 Feign说明 引入依赖启动类开启客户端 Feign接口开发 编写容错类 在业务层调用Feign客户端接口 Feign的常用属性如下 Feign说明 Feign是一种声明式.模板化的HTTP客户端.在spring cloud中使用Feign,可以做到类似于普通的接口的请求调用,可以发现对应的服务的接口,进而直接调用对应服务中的接口. 引入依赖启动类开启客户端 首先需要引入依赖 <dependency> <groupId>org.springframework.cloud<

  • Java设计模式中的七大原则详细讲解

    目录 1.开闭原则(软件设计第一原则) 2.依赖倒置原则 3.里氏替换原则 4.合成复用原则 5.接口隔离原则 6.迪米特法则 7.单一职责原则 设计模式要进行共性与可变性的分析,对共性进行抽象,同时对可变性进行封装,没有完美的设计模式,作为一名开发者要懂得取舍,触类旁通,开发出高内聚.低耦合.灵活性更高的软件产品 1.开闭原则(软件设计第一原则) 定义:一个软件实体应该对扩展开放,对修改关闭,即在不修改源代码的基础上扩展软件功能 本质思想: 以抽象来固定不变的东西(把固定不变的抽出来) 使用具

  • Golang设计模式中抽象工厂模式详细讲解

    目录 抽象工厂模式 概念示例 抽象工厂模式 抽象工厂模式是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类. 抽象工厂定义了用于创建不同产品的接口, 但将实际的创建工作留给了具体工厂类. 每个工厂类型都对应一个特定的产品变体. 在创建产品时, 客户端代码调用的是工厂对象的构建方法, 而不是直接调用构造函数 (new操作符). 由于一个工厂对应一种产品变体, 因此它创建的所有产品都可相互兼容. 客户端代码仅通过其抽象接口与工厂和产品进行交互. 该接口允许同一客户端代码与不同产品

  • RUST异步流处理方法详细讲解

    目录 Stream 特质 yield 匿名流 try_join select SELECT宏几个条件 async 问号使用 Send trait Stream 特质 在同步Rust 中流的核心是Iterator 提供了一种在序列中产生项的方法,并在它们之间进行阻塞,通过迭代器传递给其他迭代器 在异步Rust中流的核心Stream, 允许其他任务在当前阻塞等待时允许 Read/Write, AsyncRead/AsyncWrite fn main() { let f = file::create(

随机推荐