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() {@Overridepublic 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方法连接起来,使任务携带结果并随时取出结果成为可能。