【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) ——适配器设计模式的应用

适配器模式三角色:AdapteeTargetAdapter

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,ExecutionException
V get(long timeout,TimeUnit unit)throws InterruptedException,
             ExecutionException,
             TimeoutException
boolean cancel(boolean mayInterruptIfRunning)
boolean isCancelled()
boolean isDone() 
get():如果任务已经完成,get会立即返回或者抛出异常;如果任务没有完成,get会阻塞直到它完成。
ExecutionException:


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);
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值