携带结果的任务 Callable 与 Future
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();
}
}
}
以上的代码从两个方面提高了页面渲染器的性能:
- 为每一幅图像的下载都创建一个独立任务,并在线程池中执行它们,从而将串行的下载过程转换为并行的过程:这将减少下载所有图像的总时间。
- 通过从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的更多相关文章
- java多线程系类:JUC线程池:06之Callable和Future(转)
概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...
- Java多线程系列--“JUC线程池”06之 Callable和Future
概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...
- Java线程(七):Callable和Future
转自:http://blog.csdn.net/ghsau/article/details/7451464 本篇说明的是Callable和Future,它俩很有意思的,一个产生结果,一个拿到结果. C ...
- Java并发编程:Callable、Future和FutureTask
作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置 ...
- 【原创】JAVA并发编程——Callable和Future源码初探
JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程.其中前两种方式线程执行完后都没 ...
- Java多线程21:多线程下的其他组件之CyclicBarrier、Callable、Future和FutureTask
CyclicBarrier 接着讲多线程下的其他组件,第一个要讲的就是CyclicBarrier.CyclicBarrier从字面理解是指循环屏障,它可以协同多个线程,让多个线程在这个屏障前等待,直到 ...
- Callable、Future、RunnableFuture、FutureTask的原理及应用
1. Callable.Future.RunnableFuture.FutureTask的继承关系 在多线程编程中,我们一般通过一个实现了Runnable接口的对象来创建一个线程,这个线程在内部会执行 ...
- 并发编程 05—— Callable和Future
Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...
- java核心知识点学习----创建线程的第三种方式Callable和Future CompletionService
前面已经指出通过实现Runnable时,Thread类的作用就是将run()方法包装成线程执行体,那么是否可以直接把任意方法都包装成线程执行体呢?Java目前不行,但其模仿者C#中是可以的. Call ...
随机推荐
- [Luogu 2596] ZJOI2006 书架
[Luogu 2596] ZJOI2006 书架 第一次指针写 FHQ_Treap(省选噩梦数据结构)AC 啦! 省选试机写它,紧张过度失败了. 省选 Day 1 考场写它,写挂了. 省选 Day 1 ...
- 【51NOD-0】1081 子段求和
[算法]树状数组(区间和) [题解]记得开long long #include<cstdio> #include<cstring> #include<algorithm& ...
- SSH ERROR: Too many Authentication Failures
来自: How to recover from "Too many Authentication Failures for user root" 其中一种可以解决的方式 eval ...
- iOS多线程
iOS开发Demo(示例程序)源代码
本系列所有开发文档翻译链接地址:iOS7开发-Apple苹果iPhone开发Xcode官方文档翻译PDF下载地址(2013年12月29日更新版) iOS程序源代码下载链接:01.大任务.zip22 ...
- PAT L1-009 N个数求和(运用GCD进行通分)
题目链接:https://www.patest.cn/contests/gplt/L1-009 题目: 本题的要求很简单,就是求N个数字的和.麻烦的是,这些数字是以有理数“分子/分母”的形式给出的,你 ...
- idea如何搭建springmvc4
1.推荐大牛博客 此操作我操作了三次过后终于成功了,奉献大牛博客连接:做的非常详细到位,望各位采纳,推荐置顶. https://www.cnblogs.com/chenlinghong/p/83395 ...
- Commonjs,AMD,CMD和UMD的差异
CommonJS 一种服务器端模块化的规范,Nodejs实现了这种规范,所以就说Nodejs支持CommonJS. CommonJS分为三部分: require 模块加载 exports 模块导出 m ...
- jq_从右向右的滑入滑出效果
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- ThinkPHP5 模型 - 事务支持
使用事务之前,先确保数据库的存储引擎支持事务操作. MyISAM:不支持事务,主要用于读数据提高性能 InnoDB:支持事务.行级锁和并发 Berkeley DB:支持事务 ThinkPHP5 使用事 ...
- 使用cURL POST上传文件
使用cURL POST上传文件 http://blog.csdn.net/v6543210/article/details/20152575