Callable和Runnable的区别
@FunctionalInterface
public interface Runnable {
//void方法没有返回值
public abstract void run();
}
@FunctionalInterface
public interface Callable<V> {
//有返回值,并且能抛出异常
V call() throws Exception;
}
Future的主要功能
public interface Future<V> {
/**
方法说明:取消任务的执行。
参数:MayInterruptFrunning–如果执行此任务的线程应该被中断,则为true;否则,允许完成正在进行的任务
返回值:如果无法取消任务(通常是因为任务已正常完成),则为false;反之亦然
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
方法说明:如果此任务在正常完成之前被取消,则返回true。
返回值:如果此任务在完成之前取消,则为true
*/
boolean isCancelled();
/**
方法说明:如果此任务已完成,则返回true。完成可能是由于正常终止、异常或取消——在所有这些情况下,此方法将返回true。
返回值:如果此任务已完成,则为true
*/
boolean isDone();
/**
方法说明:如有必要,等待计算完成,然后检索其结果。
返回值:计算结果
抛出:ExecutionException–如果计算引发异常
InterruptedException–如果当前线程在等待时被中断
CancellationException-如果任务被取消
*/
V get() throws InterruptedException, ExecutionException;
/**
方法说明:如有必要,最多等待给定的时间以完成计算,然后检索其结果(如果可用)。
参数:timeout–等待的最长时间
unit–超时参数的时间单位
返回:计算结果
抛出:ExecutionException–如果计算引发异常
InterruptedException–如果当前线程在等待时被中断
TimeoutException–如果等待超时
CancellationException-如果任务被取消
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
利用 FutureTask 创建 Future
Future实际采用FutureTask实现,该对象相当于是消费者和生产者的桥梁,消费者通过 FutureTask 存储任务的处理结果,更新任务的状态:未开始、正在处理、已完成等。而生产者 拿到的 FutureTask 被转型为 Future 接口,可以阻塞式获取任务的处理结果,非阻塞式获取任 务处理状态。 FutureTask既可以被当做Runnable来执行,也可以被当做Future来获取Callable的返回结果。
使用案例:促销活动中商品信息查询
*在维护促销活动时需要查询商品信息(包括商品基本信息、商品价格、商品库存、商品图 片、商品销售状态等)。*这些信息分布在不同的业务中心,由不同的系统提供服务。如果采用同步方式,假设一个接口需要50ms,那么一个商品查询下来就需要200ms-300ms。如果使用Future改造则需要的就是最长耗时服务的接口,也就是50ms左 右。
public static void main(String[] args) {
//查询商品基本信息
FutureTask<String> ft1 = new FutureTask<>(new T1Task());
//查询商品促销价格
FutureTask<String> ft2 = new FutureTask<>(new T2Task());
//查询商品库存
FutureTask<String> ft3 = new FutureTask<>(new T3Task());
//查询商品图片
FutureTask<String> ft4 = new FutureTask<>(new T4Task());
//查询商品销售状态
FutureTask<String> ft5 = new FutureTask<>(new T5Task());
//线程池运行任务
ScheduledExecutorService executor = SpringUtils.getBean("threadPoolTaskExecutor");
executor.submit(ft1);
executor.submit(ft2);
executor.submit(ft3);
executor.submit(ft4);
executor.submit(ft5);
//获取结果
ft1.get();
ft2.get();
ft3.get();
ft4.get();
ft5.get();
//关闭线程池
executor.shutdown();
}
Future的局限性
- 并发执行多任务:Future只提供了get()方法来获取结果,并且是阻塞的。所以,除 了等待你别无他法;
- 无法对多个任务进行链式调用:如果你希望在计算任务完成后执行特定动作,比如 发邮件,但Future却没有提供这样的能力;
- 无法组合多个任务:如果你运行了10个任务,并期望在它们全部执行结束后执行特 定动作,那么在Future中这是无能为力的;
- 没有异常处理:Future接口中没有关于异常处理的方法;
CompletionService
主要功能:
CompletionService的主要功能就是一 边生成任务,一边获取任务的返回值。让两件事分开执行,任务之间不会互相阻塞,可以实现先执 行完的先取结果,不再依赖任务顺序了。
原理:
内部通过阻塞队列+FutureTask,实现了任务先完成可优先获取到,即结果按照完成先后顺序排序,内部有一个先进先出的阻塞队列,用于保存已经执行完成的Future,通过调用它的 take方法或poll方法可以获取到一个已经执行完成的Future,进而通过调用Future接口实现类 的get方法获取最终的结果
使用案例:
询价应用:向不同电商平台询价,并保存价格