Java - "JUC线程池" Callable与Future
Java多线程系列--“JUC线程池”06之 Callable和Future
Callable 和 Future 简介
Callable 和 Future 是比较有趣的一对组合。当我们需要获取线程的执行结果时,就需要用到它们。Callable用于产生结果,Future用于获取结果。
1. Callable
Callable 是一个接口,它只包含一个call()方法。Callable是一个返回结果并且可能抛出异常的任务。
为了便于理解,我们可以将Callable比作一个Runnable接口,而Callable的call()方法则类似于Runnable的run()方法。
Callable的源码如下:
public interface Callable<V> {
V call() throws Exception;
}
说明:从中我们可以看出Callable支持泛型。
2. Future
Future 是一个接口。它用于表示异步计算的结果。提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。
Future的源码如下:

public interface Future<V> {
// 试图取消对此任务的执行。
boolean cancel(boolean mayInterruptIfRunning)
// 如果在任务正常完成前将其取消,则返回 true。
boolean isCancelled()
// 如果任务已完成,则返回 true。
boolean isDone()
// 如有必要,等待计算完成,然后获取其结果。
V get() throws InterruptedException, ExecutionException;
// 如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}

说明: Future用于表示异步计算的结果。它的实现类是FutureTask,在讲解FutureTask之前,我们先看看Callable, Future, FutureTask它们之间的关系图,如下:

说明:
(01) RunnableFuture是一个接口,它继承了Runnable和Future这两个接口。RunnableFuture的源码如下:
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
(02) FutureTask实现了RunnableFuture接口。所以,我们也说它实现了Future接口。
示例和源码分析(基于JDK1.7.0_40)
我们先通过一个示例看看Callable和Future的基本用法,然后再分析示例的实现原理。

1 import java.util.concurrent.Callable;
2 import java.util.concurrent.Future;
3 import java.util.concurrent.Executors;
4 import java.util.concurrent.ExecutorService;
5 import java.util.concurrent.ExecutionException;
6
7 class MyCallable implements Callable {
8
9 @Override
10 public Integer call() throws Exception {
11 int sum = 0;
12 // 执行任务
13 for (int i=0; i<100; i++)
14 sum += i;
15 //return sum;
16 return Integer.valueOf(sum);
17 }
18 }
19
20 public class CallableTest1 {
21
22 public static void main(String[] args)
23 throws ExecutionException, InterruptedException{
24 //创建一个线程池
25 ExecutorService pool = Executors.newSingleThreadExecutor();
26 //创建有返回值的任务
27 Callable c1 = new MyCallable();
28 //执行任务并获取Future对象
29 Future f1 = pool.submit(c1);
30 // 输出结果
31 System.out.println(f1.get());
32 //关闭线程池
33 pool.shutdown();
34 }
35 }

运行结果:
4950
结果说明:
在主线程main中,通过newSingleThreadExecutor()新建一个线程池。接着创建Callable对象c1,然后再通过pool.submit(c1)将c1提交到线程池中进行处理,并且将返回的结果保存到Future对象f1中。然后,我们通过f1.get()获取Callable中保存的结果;最后通过pool.shutdown()关闭线程池。
1. submit()
submit()在java/util/concurrent/AbstractExecutorService.java中实现,它的源码如下:

public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
// 创建一个RunnableFuture对象
RunnableFuture<T> ftask = newTaskFor(task);
// 执行“任务ftask”
execute(ftask);
// 返回“ftask”
return ftask;
}

说明:submit()通过newTaskFor(task)创建了RunnableFuture对象ftask。它的源码如下:
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
2. FutureTask的构造函数
FutureTask的构造函数如下:

public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
// callable是一个Callable对象
this.callable = callable;
// state记录FutureTask的状态
this.state = NEW; // ensure visibility of callable
}

3. FutureTask的run()方法
我们继续回到submit()的源码中。
在newTaskFor()新建一个ftask对象之后,会通过execute(ftask)执行该任务。此时ftask被当作一个Runnable对象进行执行,最终会调用到它的run()方法;ftask的run()方法在java/util/concurrent/FutureTask.java中实现,源码如下:

public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
// 将callable对象赋值给c。
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 执行Callable的call()方法,并保存结果到result中。
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
// 如果运行成功,则将result保存
if (ran)
set(result);
}
} finally {
runner = null;
// 设置“state状态标记”
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}

说明:run()中会执行Callable对象的call()方法,并且最终将结果保存到result中,并通过set(result)将result保存。
之后调用FutureTask的get()方法,返回的就是通过set(result)保存的值。
Java - "JUC线程池" Callable与Future的更多相关文章
- Java - "JUC线程池" 架构
Java多线程系列--“JUC线程池”01之 线程池架构 概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介 ...
- Java - "JUC线程池" ThreadPoolExecutor原理解析
Java多线程系列--“JUC线程池”02之 线程池原理(一) ThreadPoolExecutor简介 ThreadPoolExecutor是线程池类.对于线程池,可以通俗的将它理解为"存 ...
- Java - "JUC线程池" 线程状态与拒绝策略源码分析
Java多线程系列--“JUC线程池”04之 线程池原理(三) 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基本概念"中,我们介绍过,线程有5种状态:新建 ...
- Java多线程系列--“JUC线程池”06之 Callable和Future
概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...
- java多线程系类:JUC线程池:03之线程池原理(二)(转)
概要 在前面一章"Java多线程系列--"JUC线程池"02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包 ...
- Java多线程系列--“JUC线程池”03之 线程池原理(二)
概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...
- java多线程系类:JUC线程池:02之线程池原理(一)
在上一章"Java多线程系列--"JUC线程池"01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我 ...
- Java多线程系列--“JUC线程池”02之 线程池原理(一)
概要 在上一章"Java多线程系列--“JUC线程池”01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我们通过分析Th ...
- Java多线程系列--“JUC线程池”04之 线程池原理(三)
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基 ...
随机推荐
- 微服务 - Eureka注册中心
我们来解决微服务的第一问题,服务的管理. 服务中心对外提供服务,需要对外暴露自己的地址.而consumer(调用者)需要记录服务提供者的地址.将来地址出现变更,还需要及时更新.这在服务较少的时候并不觉 ...
- 关于IE9 table显示错位的问题
首先,win10无法安装IE9,所以需要用IE11模拟IE9,这样:http://www.w10zj.com/Win10xy/Win10yh_638.html: 其次,table显示错位的可能原因:h ...
- 深度学习环境配置:Ubuntu16.04下安装GTX1080Ti+CUDA9.0+cuDNN7.0完整安装教程(多链接多参考文章)
本来就对Linux不熟悉,经过几天惨痛的教训,参考了不知道多少篇文章,终于把环境装好了,每篇文章或多或少都有一些用,但没有一篇完整的能解决我安装过程碰到的问题,所以决定还是自己写一篇我安装过程的教程, ...
- 一步步Cobol 400 上手自学入门教程01 - 基础概念
先学习基础概念 1.COBOL字符:包含: User-defined words 用户定义字符 System-names Reserved words 关键字 2.用户定义字符User-defin ...
- javascript数据结构与算法---二叉树(删除节点)
javascript数据结构与算法---二叉树(删除节点) function Node(data,left,right) { this.data = data; this.left = left; t ...
- odoo开发笔记 -- 后台代码什么时候需要注意编码格式
(1)首先py文件中的注释 中文汉字 一定要加u' ' (2) 前端视图不是多对一的时候,只是普通的字符字段 后台取值 需要加 str装换 head_node_dic['ManualNo'] = st ...
- hander消息机制原理(looper轮询监听机制)
基本原理 线程中调用Handler.sendMsg()方法(参数是Message对象),将需要Main线程处理的事件 添加到Main线程的MessageQueue中,Main线程通过MainLoope ...
- syslog之二:syslog协议及rsyslog服务全解析
目录: <syslog之一:Linux syslog日志系统详解> <syslog之二:syslog协议及rsyslog服务全解析> <syslog之三:建立Window ...
- Git使用(1)
安装git完成后 1.首先配置你的用户信息,用于体现在你的提交记录中包含your name and your email git config --global user.name "you ...
- Mysql的优化一则
目的在于这么一个sql语句: SELECT w.* FROM wallpaper w inner join wallpaper_category_relation r ON w.wallpaper_i ...