概要

本章介绍线程池中的Callable和Future。
Callable 和 Future 简介
示例和源码分析(基于JDK1.7.0_40)

转载请注明出处:http://www.cnblogs.com/skywang12345/p/3544116.html

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线程池:06之Callable和Future(转)的更多相关文章

  1. Java多线程系列--“JUC线程池”06之 Callable和Future

    概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...

  2. java多线程系类:JUC线程池:02之线程池原理(一)

    在上一章"Java多线程系列--"JUC线程池"01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我 ...

  3. java多线程系类:JUC线程池:01之线程池架构

    概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介绍JUC的最后一部分的内容--线程池.内容包括:线程池架构 ...

  4. java多线程系类:JUC线程池:03之线程池原理(二)(转)

    概要 在前面一章"Java多线程系列--"JUC线程池"02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包 ...

  5. java多线程详解(7)-线程池的使用

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, 这样频繁创建线程就会大大降低系 ...

  6. java多线程总结五:线程池的原理及实现

    1.线程池简介:     多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力.        假设一个服务器完成一项任务所需时间为:T1 创 ...

  7. (Java多线程系列九)线程池

    线程池 1.什么是线程池 线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程.线程池中线程的数量通常取决于可用内存数量和应用程序的需求. ...

  8. java多线程系类:JUC线程池:04之线程池原理(三)(转)

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--"基础篇& ...

  9. java多线程系类:JUC线程池:05之线程池原理(四)(转)

    概要 本章介绍线程池的拒绝策略.内容包括:拒绝策略介绍拒绝策略对比和示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3512947.html 拒绝策略 ...

随机推荐

  1. 表单reset无法重置hidden的解决方案

    方法一:用text代替hidden,设置text隐藏 <input id="id" name="id" style="display: none ...

  2. Net中的常见的关键字

    Net中的关键字有很多,我们最常见的就有new.base.this.using.class.struct.abstract.interface.is.as等等.有很多的,在这里就介绍大家常见的,并且有 ...

  3. 学习SpringMVC——国际化+上传+下载

    每个星期一道菜,这个星期也不例外~~~ 一个软件,一个产品,都是一点点开发并完善起来的,功能越来越多,性能越来越强,用户体验越来越好……这每个指标的提高都需要切切实实的做点东西出来,好比,你的这个产品 ...

  4. [入门级] visual studio 2010 mvc4开发,用ibatis作为数据库访问媒介(一)

    [入门级] visual studio 2010 mvc4开发,用ibatis作为数据库访问媒介(一) Date  周二 06 一月 2015 By 钟谢伟 Tags mvc4 / asp.net 示 ...

  5. 支撑Java NIO 与 NodeJS的底层技术

    支撑Java NIO 与 NodeJS的底层技术 众所周知在近几个版本的Java中增加了一些对Java NIO.NIO2的支持,与此同时NodeJS技术栈中最为人称道的优势之一就是其高性能IO,那么我 ...

  6. 中文分词工具探析(一):ICTCLAS (NLPIR)

    1. 前言 ICTCLAS是张华平在2000年推出的中文分词系统,于2009年更名为NLPIR.ICTCLAS是中文分词界元老级工具了,作者开放出了free版本的源代码(1.0整理版本在此). 作者在 ...

  7. 总结:如何使用redis缓存加索引处理数据库百万级并发

    前言:事先说明:在实际应用中这种做法设计需要各位读者自己设计,本文只提供一种思想.准备工作:安装后本地数redis服务器,使用mysql数据库,事先插入1000万条数据,可以参考我之前的文章插入数据, ...

  8. js数组去重

    这就是数组去重了...var str=['hello','node','element','node','hello','blue','red'];var str1=[]; function firs ...

  9. SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.

    相信很多人进行数据存储时,会遇上如标题的异常错误. 其实也不算上一个错误. 当你的程序中有宣告一个字段的数据类型为DateTime时,但你又没有赋值给它,就进行存储时,它就会得到这样一个结果. 看看下 ...

  10. WCF学习系列二---【WCF Interview Questions – Part 2 翻译系列】

    http://www.topwcftutorials.net/2012/09/wcf-faqs-part2.html WCF Interview Questions – Part 2 This WCF ...