【Java并发】可携带结果的任务:Callable和Future
任务
?? ? ?Executor框架使用Runnable作为任务的基本抽象。Runnable确实有限的抽象,虽然能够产生一些边界效应,如记录日志文件或者将结果写入一个共享数据结构里,但是不能返回一个值或者抛出检测异常。
?? ? 很多任务会引起严重的计算延迟——执行DB查询,从网络上获取资源,复杂计算等。对于这些任务,Callable是更佳的抽象。
?? ? Runnable和Callable描述的是抽象的计算型任务,这些任务通常是有限的:有一个明确的开始点,而且最终会结束。一个Executor执行的任务的生命周期可有四个阶段:
created -->?submitted -->?started -->?completed.
?
Callable?
包:java.util.concurrent类:public interface Callable<V>方法:V call() throws Exception
?
?? ? ?Callable Task是有lifecycle的,分别为created/submitted/started/completed(创建/提交/启动/完成)。
?? ? ?对于处在submitted与started间的task可以通过Future来cancel该task,对于已经started但是还没有completed的task,只能期望task能够响应interruption从而终止(Future依然调用cancel)。对于已经completed的task是无法被cancel的,此时如果调用cancel则会返回false(其实如果调用cancel返回false一般是因为该task已经completed)。
任务封装?? ? ? Executors提供了些工具方法用于将其他类型的任务(如Runnable、java.security.PrivilegedAction)封装成一个Callable。 public class Executors extends Object
//返回 Callable 对象,调用它时可运行给定的任务并返回 null。static Callable<Object> callable(Runnable task) //返回 Callable 对象,调用它时可运行给定的任务并返回给定的结果。static <T> Callable<T> callable(Runnable task, T result)【扩展】?Callable?callable(Runnable task, T result) ——适配器设计模式的应用
public static Callable<Object> callable(Runnable task) { if (task == null) throw new NullPointerException(); return new RunnableAdapter<Object>(task, null);}public static <T> Callable<T> callable(Runnable task, T result) { if (task == null) throw new NullPointerException(); return new RunnableAdapter<T>(task, result);}//Adapter类:实现目标接口,持有被适配者adaptee的引用,通过构造将被适配者实例传入static final class RunnableAdapter<T> implements Callable<T> { final Runnable task; final T result; RunnableAdapter(Runnable task, T result) { this.task = task; this.result = result; } public T call() { task.run(); return result; }}?
Future?? ? ?描述了asynchronous computation task任务的生命周期,提供了如下功能 1.获取任务的结果 2.取消任务 3.获得任务进行状态(完成还是被取消)。
public interface Future<V>V get() throws InterruptedException,ExecutionExceptionV get(long timeout,TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutExceptionboolean cancel(boolean mayInterruptIfRunning)boolean isCancelled()boolean isDone()get():如果任务已经完成,get会立即返回或者抛出异常;如果任务没有完成,get会阻塞直到它完成。ExecutionException:
Future继承结构

方式1:将Callable或者Runnable提交给executor获得Future实例。
?
ExecutorService executor = ...Future f = executor.submit(Callable<T> task);Future f = executor.submit(Runnable task);Future f = executor.submit(Runnable task, T result);
方式2:使用给定的Callable或者Runnable来实例化FutureTask。
?
FutureTask已实现的接口:Runnable、Future、RunnableFuture。
构造:可以使用FutureTask来包装Callable或Runnable对象。
?
//FutureTask一旦运行就执行给定的 CallableFutureTask(Callable<V> callable) FutureTask(Runnable runnable, V result)
要点:
1. 因为FutureTask实现了Runnable接口,可以可将FutureTask交由Executor执行,或者构造一个Thread。
2.?FutureTask的计算结果从运行计算的线程传送到需要这个结果的线程(调用get的线程),并保证这种传递建立在结果的安全发布基础上。
?
代码示例:利用FutureTask、Callable、Thread对耗时任务(如查询数据库)做预处理,在需要计算结果之前就启动计算。
(代码见ConcurrentSamples/simulate.futuretask.preload)
private final FutureTask<ProductInfo> future = new FutureTask<ProductInfo>(new Callable<ProductInfo>(){ public ProductInfo call() throws DataLoadException{ ProductInfoLoader loader = new ProductInfoLoader(); return loader.load();//加载数据:查询数据库 } }); private final Thread t = new Thread(future); public void start(){ t.start(); } public ProductInfo load() throws DataLoadException, InterruptedException{ try { return future.get(); } catch (ExecutionException e) { Throwable cause = e.getCause(); if(cause instanceof DataLoadException) throw (DataLoadException)cause; else throw launderThrowable(e); } }