Future模式是多线程并发编程的扩展,这种方式支持返回结果,使用get()方法阻塞当前线程。它的核心思想是异步调用,当我们执行某个函数时,它可能很慢,但是我们又不着急要结果。因此,我们可以让它立即返回,让它异步去执行这个请求,当我们需要结果时,阻塞调用线程获取结果。
一个Future<V>
接口表示一个未来可能会返回的结果,它定义的方法有:
get()
:获取结果(可能会等待)get(long timeout, TimeUnit unit)
:获取结果,但只等待指定的时间;cancel(boolean mayInterruptIfRunning)
:取消当前任务;isDone()
:判断任务是否已完成。
商品查询
一个简单的生产实践例子,在维护促销活动时需要查询商品信息(包括商品基本信息、商品价格、商品库存、商品图片、商品销售状态等)。这些信息分布在不同的业务中心,由不同的系统提供服务。假设一个接口需要50ms,那么一个商品查询下来就需要200ms-300ms,这对于我们来说时不满意的。如果使用Future改造则需要的就是最长耗时服务的接口,也就是50ms左右。
当然这里并不能解决的是一个接口服务突然很慢的问题,如果要解决这个问题,需要辅助其他组件(流控,降级等)。
伪代码如下:
1 | java复制代码public class FutureTest { |
工作原理
增强的Future:CompletableFuture
CompletableFuture时Java8新增的一个超大型工具类。它不仅实现了Future接口,还实现了CompletionStage接口,该接口总共拥有40多种方法(为了函数式编程中的流程调用准备的)。
使用Future
获得异步执行结果时,要么调用阻塞方法get()
,要么轮询看isDone()
是否为true
,这两种方法都不是很好,因为主线程也会被迫等待。
CompletableFuture
针对Future
做了改进,可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法。
简单示例:
1 | java复制代码public class CompletableFutureDemo { |
创建一个CompletableFuture
是通过CompletableFuture.supplyAsync()
实现的,它需要一个实现了Supplier
接口的对象:
1 | java复制代码public interface Supplier<T> { |
这里我们用lambda语法简化了一下,直接传入CompletableFutureDemo::fetchPrice
,因为CompletableFutureDemo.fetchPrice()
静态方法的签名符合Supplier
接口的定义(除了方法名外)。
紧接着,CompletableFuture
已经被提交给默认的线程池执行了,我们需要定义的是CompletableFuture
完成时和异常时需要回调的实例。完成时,CompletableFuture
会调用Consumer
对象:
1 | java复制代码public interface Consumer<T> { |
异常时,CompletableFuture
会调用:
1 | java复制代码public interface Function<T, R> { |
可见CompletableFuture
的优点是:
- 异步任务结束时,会自动回调某个对象的方法;
- 异步任务出错时,会自动回调某个对象的方法;
- 主线程设置好回调后,不再关心异步任务的执行。
除此之外,
CompletableFuture
还允许将多个CompletableFuture
进行组合。
CompletionStage接口:
描述and汇聚关系:
- thenCombine:任务合并,有返回值
- thenAccepetBoth:两个任务执行完成后,将结果交给thenAccepetBoth消耗,无返回值。
- runAfterBoth:两个任务都执行完成后,执行下一步操作(Runnable)。
描述or汇聚关系
- applyToEither:两个任务谁执行的快,就使用那一个结果,有返回值。
- acceptEither: 两个任务谁执行的快,就消耗那一个结果,无返回值。
- runAfterEither: 任意一个任务执行完成,进行下一步操作(Runnable)。
CompletableFuture类自己也提供了anyOf()和allOf()用于支持多个CompletableFuture
并行执行。
重写商品查询
1 | java复制代码public class CompletableFutureTest { |
CompletableFuture还有很多花样,毕竟有那么多接口方法,留待读者们自己去尝试吧。感谢阅读!
本文转载自: 掘金