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

在Executor接口中只定义了execute方法,而submit方法则是在ExecutorService接口中定义的。
//ExecutorService
public interface ExecutorService extends Executor {
...
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
<T> Future<T> submit(Runnable task);
...
}
而在其子类AbstractExecutorService实现了submit方法。
//AbstractExecutorService
public abstract class AbstractExecutorService implements ExecutorService {
...
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerExeption();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
...
}
在AbstractExecutorService实现的submit方法实际上是一个模板方法,定义了submit方法的算法骨架,其execute交给了子类。(可以看到在很多源码中,模板方法模式被大量运用,有关模板方法模式可参考《模板方法模式》)
尽管submit方法能提供线程执行的返回值,但只有实现了Callable才会有返回值,而实现Runnable的线程则是没有返回值的,也就是说在上面的3个方法中,submit(Callable<T> task)能获取到它的返回值,submit(Runnable task, T result)能通过传入的载体result间接获得线程的返回值或者准确来说交给线程处理一下,而最后一个方法submit(Runnable task)则是没有返回值的,就算获取它的返回值也是null。
下面给出3个例子,来感受下submit方法。
submit(Callable<T> task)
package com.threadpoolexecutor; import java.util.concurrent.*; /**
* ThreadPoolExecutor#sumit(Callable<T> task)
* Created by yulinfeng on 6/17/17.
*/
public class Sumit1 { public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<String> callable = new Callable<String>() {
public String call() throws Exception {
System.out.println("This is ThreadPoolExetor#submit(Callable<T> task) method.");
return "result";
}
}; ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(callable);
System.out.println(future.get());
}
}
submit(Runnable task, T result)
package com.threadpoolexecutor; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; /**
* ThreadPoolExecutor#submit(Runnable task, T result)
* Created by yulinfeng on 6/17/17.
*/
public class Submit2 { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executor = Executors.newSingleThreadExecutor();
Data data = new Data();
Future<Data> future = executor.submit(new Task(data), data);
System.out.println(future.get().getName());
}
} class Data {
String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
} class Task implements Runnable {
Data data; public Task(Data data) {
this.data = data;
}
public void run() {
System.out.println("This is ThreadPoolExetor#submit(Runnable task, T result) method.");
data.setName("kevin");
}
}
submit(Runnable task)
package com.threadpoolexecutor; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; /**
* ThreadPoolExecutor#sumit(Runnable runnables)
* Created by yulinfeng on 6/17/17.
*/
public class Submit { public static void main(String[] args) throws ExecutionException, InterruptedException {
Runnable runnable = new Runnable() {
public void run() {
System.out.println("This is ThreadPoolExetor#submit(Runnable runnable) method.");
}
}; ExecutorService executor = Executors.newSingleThreadExecutor();
Future future = executor.submit(runnable);
System.out.println(future.get());
}
}
通过上面的实例可以看到在调用submit(Runnable runnable)的时候是不需要其定义类型的,也就是说虽然在ExecutorService中对其定义的是泛型方法,而在AbstractExecutorService中则不是泛型方法,因为它没有返回值。(有关Object、T、?这三者的区别,可参考《Java中的Object、T(泛型)、?区别》)。
从上面的源码可以看到,这三者方法几乎是一样的,关键就在于:
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
它是如何将一个任务作为参数传递给了newTaskFor,然后调用execute方法,最后进而返回ftask的呢?
//AbstractExecutorService#newTaskFor
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
看来是返回了一个FutureTask实例,FutureTask实现了Future和Runnable接口。Future接口是Java线程Future模式的实现,可用用来异步计算,实现Runnable接口表示可以作为一个线程执行。FutureTask实现了这两个接口意味着它代表异步计算的结果,同时可以作为一个线程交给Executor来执行。有关FutureTask放到下章来单独解析。所以本文对于线程池ThreadPoolExecutor线程池的submit方法解析并不完整,必须得了解Java线程的Future模式——《14.Java中的Future模式》。
13.ThreadPoolExecutor线程池之submit方法的更多相关文章
- 线程池中 submit()和 execute()方法有什么区别?(未完成)
线程池中 submit()和 execute()方法有什么区别?(未完成)
- [转]ThreadPoolExecutor线程池的分析和使用
1. 引言 合理利用线程池能够带来三个好处. 第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 第二:提高响应速度.当任务到达时,任务可以不需要等到线程创建就能立即执行. 第 ...
- ThreadPoolExecutor 线程池的源码解析
1.背景介绍 上一篇从整体上介绍了Executor接口,从上一篇我们知道了Executor框架的最顶层实现是ThreadPoolExecutor类,Executors工厂类中提供的newSchedul ...
- 论如何优雅的自定义ThreadPoolExecutor线程池
更好的markDown阅读体验可直接访问我的CSDN博客:https://blog.csdn.net/u012881584/article/details/85221635 前言 线程池想必大家也都用 ...
- ThreadPoolExecutor线程池的分析和使用
1. 引言 合理利用线程池能够带来三个好处. 第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 第二:提高响应速度.当任务到达时,任务可以不需要等到线程创建就能立即执行. 第 ...
- ThreadPoolExecutor 线程池浅析
作为Executor框架中最核心的类,ThreadPoolExecutor代表着鼎鼎大名的线程池,它给了我们足够的理由来弄清楚它. 下面我们就通过源码来一步一步弄清楚它. 内部状态 线程有五种状态:新 ...
- j.u.c系列(01) ---初探ThreadPoolExecutor线程池
写在前面 之前探索tomcat7启动的过程中,使用了线程池(ThreadPoolExecutor)的技术 public void createExecutor() { internalExecutor ...
- 线程池之ThreadPoolExecutor线程池源码分析笔记
1.线程池的作用 一方面当执行大量异步任务时候线程池能够提供较好的性能,在不使用线程池的时候,每当需要执行异步任务时候是直接 new 一线程进行运行,而线程的创建和销毁是需要开销的.使用线程池时候,线 ...
- Java并发——ThreadPoolExecutor线程池解析及Executor创建线程常见四种方式
前言: 在刚学Java并发的时候基本上第一个demo都会写new Thread来创建线程.但是随着学的深入之后发现基本上都是使用线程池来直接获取线程.那么为什么会有这样的情况发生呢? new Thre ...
随机推荐
- [进程管理] Linux中Load average的理解
Load average的定义 系统平均负载被定义为在特定时间间隔内运行队列中的平均进程树.如果一个进程满足以下条件则其就会位于运行队列中: - 它没有在等待I/O操作的结果 - 它没有主动进入等待状 ...
- 转Fiddler 构造http请求
今天使用Fiddler构造一个POST请求,server端的PHP脚本的 $_POST数组中怎么也获取不到值,后来偶然发现是因为缺少了一个http头:Content-Type: application ...
- iOS开发 - Swift使用JavaScriptCore与JS交互
一.前言 在这个提倡敏捷开发和H5横行的年代,原生App内嵌入一些H5页面已经成为一种流行的趋势.一套H5页面就可以适配复杂的iOS和Android页面,大量节省了开发和维护时间,如果本来就有移动端网 ...
- Display:table;妙用,使得左右元素高度相同
我们在设计网页的时候,为了左右能够分明一点,我们经常会在左边元素弄一个border-right,但是出现一个问题,如果左边高度比较小,这根线就短了,下面空了一部分,反正如果在右边的元素弄一个borde ...
- Android库的标准化(不断更新中)
在Android的开发过程中,为了代码的通用性,模块化等原因,应该尽量将功能独立的,通用性高的模块抽离出来,建立单独的库.各个库的功能不同,但是同样作为库,依然有很多相通的地方. 各个库应该都应该遵循 ...
- 关于Yii框架的基础知识
第一次写博文,也不知道怎么写,不太熟悉,带小伙伴学习一样我日常使用的Yii框架. PHP中的开发框架有很多,比如:ThinkPHP.Yii.CI.Laravel.Phalcon等.现在流行度最高的是L ...
- spring boot入门
一.对spring boot的解释:(百度百科) Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行 ...
- JEESZ-Redis分布式缓存安装和使用
独立缓存服务器: Linux CentOS Redis 版本: 3.0下面我们针对于Redis安装做下详细的记录:编译和安装所需的包:# yum install gcc tcl创建安装目录:# mkd ...
- bitnami gitlab 安装
安装gitlab需要安装的依赖软件比较多,基于偷懒的原则,从网上找到了bitnami-gitlab-8.7.1-0-linux-x64-installer.run ,集成了所有的相关软件,一键安装,省 ...
- B计划
简介:从一个初学者的角度来讲,要从六个方面来对计算机来做一个大约的了解: 计算机的组成: 电脑配置: 操作系统: CDEF盘: 正确的开关机(主要是关机): 常用软件: 1. 电脑由哪几部分组成? ...