ThreadPoolExecutor中的submit()方法详细讲解
作者:张行之 发布时间:2022-02-18 03:02:39
在使用线程池的时候,发现除了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---------------------
主线程会一直阻塞,等待线程池中的任务执行完后,在执行后面的语句。
来源:https://blog.csdn.net/qq_33689414/article/details/72955253


猜你喜欢
- 1.构造器也就是在上一篇讲的那个例子,调用默认的无参构造函数2.静态工厂方法1)创建需要执行的方法的类public class HelloW
- StreamAPI中的stream不能被重复消费,一旦它被使用,stream就被关闭了,别的地方再消费它就会抛IllegalStateExc
- 1.什么是反射?一个类有多个组成部分,例如:成员变量,方法,构造方法等。反射就是加载类,并解剖出类的各个组成部分。2.加载类java中有一个
- 使用java来实现一个简单的计算器,供大家参考,具体内容如下最近使用java制作了一个计算器,菜单栏里的功能没有实现,只是写一个版式,仅仅实
- 在实际项目中,Java有时候需要调用C写出来的东西,除了JNI以外,我认为一种比较好的方法是JAVA调用Shell。先把C写出来的make成
- Feature: 点击选择拍照或者打开相册,选取图片进行裁剪最后设置为圆形头像。Problem: 拍好照片,点击裁剪,弹Toast“无法加载
- 使用场景:自己项目对接多个外部系统,各个外部系统使用的字段并没有统一,所以要根据不同系统动态的输出序列化数据,使适应各个系统的要求实现方式使
- 一、java异常总结:异常就是程序运行时出现不正常运行情况1.异常由来:通过java的类的形式对现实事物中问题的描述,并封住成了对象其实就是
- 本文实例为大家分享了Java NIO实战之多人聊天室的具体代码,供大家参考,具体内容如下NIO服务端public class NioServ
- 一、线程优先级的介绍java 中的线程优先级的范围是1~10,默认的优先级是5。“高优先级线程”会优先于“低优先级线程”执行。java 中有
- 背景介绍在开发应用过程中经常会遇到显示一些不同的字体风格的信息犹如默认的LockScreen上面的时间和充电信息。对于类似的情况,可能第一反
- 导入maven项目各个注解均报错所遇问题导入maven项目各个注解均报错了思考1:这个项目使用了springboot;spring是个”大容
- 本文实例讲述了C#使用listView增删操作的方法。分享给大家供大家参考。具体分析如下:应用场景: C#中使用listView控件,实现动
- 该文章中使用了较多的 委托delegate和Lambda表达式,如果你并不熟悉这些,请查看我的文章《委托与匿名委托》、《匿名委托与Lambd
- 这篇文章主要介绍了Java如何把int类型转换成byte,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的
- 前言之前学习的顺序表查询非常快,时间复杂度为O(1),但是增删改效率非常低,因为每一次增删改都会元素的移动。可以使用另一种存储方式-链式存储
- 1. dip: device independent pixels(设备独立像素). 不同设备有不同的显示效果,这个和设备硬件有关,一般我们
- 闲来无事,用C++做了一个图书管理系统,主要有借书、还书、图书管理、用户管理等功能,主要用到的技术有容器和文件,以及类的封装#include
- 前言本篇文章主要介绍的是SpringBoot项目进行全局异常的处理。SpringBoot全局异常准备说明:如果想直接获取工程那么可以直接跳到
- Android ViewPager2 UsageViewPager2 是 ViewPager 的升级版本,解决了 ViewPager 的大部