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. 数据结构:二维ST表

    POJ2019 我们其实是很有必要把ST算法拓展到二维的,因为二维的RMQ问题还是不少的 int N,B,K; ]; int val[maxn][maxn]; ][]; ][]; 这里的N是方阵的长宽 ...

  2. 爬虫--BeautifulSoup

    什么是BeautifulSoup? BeautifulSoup支持的一些解析库 基本使用 from bs4 import BeautifulSoup html =""" ...

  3. javascript工厂模式、单例模式

    //工厂模式 function createObject(name,age){ var obj = new Object(); obj.name = name; obj.age = age; obj. ...

  4. perl模拟登录(1)

    use WWW::Mechanize; my $ua = WWW::Mechanize->new(); $ua->post('http://localhost/dvwa/DVWA-mast ...

  5. 虚拟机出现intel vt -x 处于禁用状态打不开处理方式

    处理方式 . 1 进入bios 以华硕主板为例 进入高级模式找到cpu虚拟技术 打开虚拟技术支持 其它电脑找到这个

  6. mount/umount命令【转】

    转自:http://www.cnblogs.com/qq78292959/archive/2012/03/06/2382334.html 如果想在运行的Linux下访问其它文件系统中的资源的话,就要用 ...

  7. 使用cURL POST上传文件

    使用cURL POST上传文件 http://blog.csdn.net/v6543210/article/details/20152575

  8. Mysql 数据库学习笔记04 函数

    一.创建自定义函数 * 使用自定义函数,可以返回字符串.整型.实数或者其他类型: create [aggregate] function 名称 (参数列表) return type begin //函 ...

  9. Linux下批量Kill多个进程的方法

    转自http://www.jb51.net/LINUXjishu/43534.html ps -ef|grep tt.py|grep -v grep|cut -c 9-15|xargs kill -9 ...

  10. Foreach与迭代器

    Foreach与迭代器 Foreach语句可以用于数组和集合的遍历.之所以能够工作,是因为Java SE5引入了新的被称为Iterable的接口,该接口中包含一个能够产生Iterator的iterat ...