任务
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)。
//返回 Callable 对象,调用它时可运行给定的任务并返回 null。
static Callable<Object> callable(Runnable task)
//返回 Callable 对象,调用它时可运行给定的任务并返回给定的结果。
static <T> Callable<T> callable(Runnable task, T result)
适配器模式三角色:Adaptee、Target、Adapter
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
public interface Future<V>
V get() throws InterruptedException,ExecutionException
V get(long timeout,TimeUnit unit)throws InterruptedException,
ExecutionException,
TimeoutException
boolean cancel(boolean mayInterruptIfRunning)
boolean isCancelled()
boolean isDone()
Future继承结构
创建描述任务的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一旦运行就执行给定的 Callable
FutureTask(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);
}
}