Executor框架使用Runnable作为其基本任务表示形式。Runnable是一种有很大局限的抽象,它不能返回一个值或者抛出一个受检查的异常。

但是许多任务实际上都是存在延迟的计算,比如执行数据库查询,从网络上获取资源,或者计算某个复杂的功能。对于这些任务,就要Callable来显身手了。

public interface Callable<V>{
V call() throws Exception;
}

Future表示一个任务的生命周期,并提供了相应的方法来判断是否已经完成或者取消,以及获取任务的结果和取消任务等。

public interface Future<V> {

    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}

ExecutorService中所有submit方法都将返回一个Future,从而将一个Runnable或Callable提交给Executor,并得到一个Future来获得任务执行结果或者取消任务。

示例:使用Future实现页面渲染器

public class FutureRenderer {

    private final ExecutorService executorService = Executors.newFixedThreadPool(5);

    public void renderPage(CharSequence source) {
final List<ImageInfo> imageInfoList = scanForImageInfo(source);
Callable<List<ImageData>> task = new Callable<List<ImageData>>() {
public List<ImageData> call() throws Exception {
List<ImageData> result = new ArrayList<ImageData>();
for (ImageInfo imageInfo:imageInfoList)
result.add(imageInfo.downloadImage());
return null;
}
};
Future<List<ImageData>> future = executorService.submit(task);
renderText(source);
try {
List<ImageData> imageDatas = future.get();
for (ImageData data : imageDatas) {
renderImage(data);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
future.cancel(true);
} catch (ExecutionException e) {
e.printStackTrace();
} }
}

get方法的行为取决于任务的状态(尚未开始、正在运行、已完成)。如果任务已经完成,那么get会立即返回或者抛出一个Exception,如果任务没有完成,那么get将阻塞并直到任务完成。

还可以显式地为某个指定的Runnable或Callable实例化一个FutureTask(由于FutureTask实现了Runnable,因此可以将它提交给Executor来执行或者直接调用它的run方法)。

 ExecutorService executorService = Executors.newFixedThreadPool(5);
FutureTask futureTask = new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
return null;
}
});
executorService.submit(futureTask);
//futureTask.run();

CompletionService

CompletionService将Executor和BlockingQueue的功能融合在一起。你可以将Callable任务提交给它来执行,然后使用类似于队列操作的take和poll等方法来获得已完成的结果。

示例:使用CompletionService实现页面渲染器

public class Renderer {

    private final ExecutorService executorService;

    public Renderer(ExecutorService executorService) {
this.executorService = executorService;
} public void renderPage(CharSequence source) {
final List<ImageInfo> imageInfoList = scanForImageInfo(source);
CompletionService<ImageData> completionService = new ExecutorCompletionService<ImageData>(executorService);
for (final ImageInfo imageInfo : imageInfoList) {
completionService.submit(new Callable<ImageData>() {
public ImageData call() throws Exception {
return imageInfo.downloadImage();
}
});
} renderText(source); try {
for (int i = 0, n = imageInfoList.size(); i < n; i++) {
Future<ImageData> f = completionService.take();
ImageData imageData = f.get();
renderImage(data);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
future.cancel(true);
} catch (ExecutionException e) {
e.printStackTrace();
} }
}

以上的代码从两个方面提高了页面渲染器的性能:

  1. 为每一幅图像的下载都创建一个独立任务,并在线程池中执行它们,从而将串行的下载过程转换为并行的过程:这将减少下载所有图像的总时间。
  2. 通过从CompletionService中获取结果以及使每张图片下载完成后立刻显示出来,能使用户获得一个更加动态和更加响应性的用户界面。

可以看摘取的部分源码:

public class ExecutorCompletionService<V> implements CompletionService<V> {
private final Executor executor;
private final AbstractExecutorService aes;
private final BlockingQueue<Future<V>> completionQueue; /**
* FutureTask extension to enqueue upon completion
*/
private class QueueingFuture extends FutureTask<Void> {
QueueingFuture(RunnableFuture<V> task) {
super(task, null);
this.task = task;
}
protected void done() { completionQueue.add(task); }
private final Future<V> task;
} public ExecutorCompletionService(Executor executor) {
if (executor == null)
throw new NullPointerException();
this.executor = executor;
this.aes = (executor instanceof AbstractExecutorService) ?
(AbstractExecutorService) executor : null;
this.completionQueue = new LinkedBlockingQueue<Future<V>>();
} public Future<V> submit(Callable<V> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task);
executor.execute(new QueueingFuture(f));
return f;
} public Future<V> submit(Runnable task, V result) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task, result);
executor.execute(new QueueingFuture(f));
return f;
} //...
}

ExecutorCompletionService实现了CompletionService,并将计算部分委托给一个Executor。在构造函数中创建一个BlockingQueue来保存计算完成的结果。

Callable和Future总是如影随形,通过一个submit方法连接起来,使任务携带结果并随时取出结果成为可能。

携带结果的任务 Callable 与 Future的更多相关文章

  1. java多线程系类:JUC线程池:06之Callable和Future(转)

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

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

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

  3. Java线程(七):Callable和Future

    转自:http://blog.csdn.net/ghsau/article/details/7451464 本篇说明的是Callable和Future,它俩很有意思的,一个产生结果,一个拿到结果. C ...

  4. Java并发编程:Callable、Future和FutureTask

    作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置 ...

  5. 【原创】JAVA并发编程——Callable和Future源码初探

    JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程.其中前两种方式线程执行完后都没 ...

  6. Java多线程21:多线程下的其他组件之CyclicBarrier、Callable、Future和FutureTask

    CyclicBarrier 接着讲多线程下的其他组件,第一个要讲的就是CyclicBarrier.CyclicBarrier从字面理解是指循环屏障,它可以协同多个线程,让多个线程在这个屏障前等待,直到 ...

  7. Callable、Future、RunnableFuture、FutureTask的原理及应用

    1. Callable.Future.RunnableFuture.FutureTask的继承关系 在多线程编程中,我们一般通过一个实现了Runnable接口的对象来创建一个线程,这个线程在内部会执行 ...

  8. 并发编程 05—— Callable和Future

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  9. java核心知识点学习----创建线程的第三种方式Callable和Future CompletionService

    前面已经指出通过实现Runnable时,Thread类的作用就是将run()方法包装成线程执行体,那么是否可以直接把任意方法都包装成线程执行体呢?Java目前不行,但其模仿者C#中是可以的. Call ...

随机推荐

  1. 搜索:BFS

    如果题目真的要考察宽度优先搜索,那么这类题目往往具有比较大的编码难度,换个说法,就是细枝末节特别多,状态特别复杂.. 剥茧抽丝,这里以一个比较“裸”的BFS作为例子,了解一下实现BFS的一些规范. 直 ...

  2. vijos 1426 背包+hash

    背景 北京奥运会开幕了,这是中国人的骄傲和自豪,中国健儿在运动场上已经创造了一个又一个辉煌,super pig也不例外……………… 描述 虽然兴奋剂是奥运会及其他重要比赛的禁药,是禁止服用的.但是运动 ...

  3. Linux系统关闭防火墙端口

    1. 打开防火墙端口 # iptables -I INPUT -p tcp --dport -j ACCEPT # iptables -I INPUT -p tcp --dport -j ACCEPT ...

  4. gradle web项目启动报错: java.lang.ClassNotFoundException: org.springframework.web.util.IntrospectorCleanupListener

    严重: Error configuring application listener of class org.springframework.web.util.IntrospectorCleanup ...

  5. 51nod 1190 最小公倍数之和 V2

    给出2个数a, b,求LCM(a,b) + LCM(a+1,b) + .. + LCM(b,b). 例如:a = 1, b = 6,1,2,3,4,5,6 同6的最小公倍数分别为6,6,6,12,30 ...

  6. Drainage Ditches(POJ1273+网络流+Dinic+EK)

    题目链接:poj.org/problem?id=1273 题目: 题意:求最大流. 思路:测板子题,分别用Dinic和EK实现(我的板子跑得时间均为0ms). Dinic代码实现如下: #includ ...

  7. 一键前端代理,一行命令开启nginx容器,代理前端页面

    我们在前端开发的过程中,在对接口时候,往往需要跨域请求,那么及其简便的方法就是使用nginx反向代理,但是存在几点缺点 1.在新的一个项目下,我们需要找到安装nginx目录的nginx.conf文件并 ...

  8. KVM初始化过程

    转载:http://blog.csdn.net/dashulu/article/details/17074675 之前打算整理一下在Guest VM, KVM, QEMU中IO处理的整个流程,通过查阅 ...

  9. mac cocoapod安装过程

    cocoapod: 自动化管理第三方开发包的一个插件, 废话不多说, 一个新手只需做如下几个步骤 1-> 安装ruby环境(可忽略, 不是必要) 1.1 首先我们先看看当前你机器上ruby的版本 ...

  10. leetcode 之Implement strStr()(27)

    字符串的匹配,返回匹配开始的位置,直接用暴力方式求解.为了更快的匹配,定义一个指针表示待匹配的字符串的长度,当长度不足时,可 直接停止匹配. char *strStr(char *haystack, ...