CompletableFuture,增强Future
Future 接口介绍
Java5 新增了 Future 接口,用于描述一个异步计算的结果。虽然 Future 以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的初衷相违背,轮询的方式又会耗费无谓的 CPU 资源,而且也不能及时地得到计算结果,为什么不能用观察者设计模式呢?即当计算结果完成及时通知监听者(回调)。
Future存在的一些短板
不能手动完成计算
调用 get() 方法会阻塞程序
不能链式执行
整合多个 Future 执行结果方式笨重
没有好的异常处理方案
CompletableFuture介绍
Java 8 中, 新增加了一个包含 50 个方法左右的类–CompletableFuture,它提供了非常强大的 Future 的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合 CompletableFuture 的方法。
CompletableFuture类结构
实现了 Future 接口
实现了 Future 接口,那就具有 Future 接口的相关特性
实现了 CompletionStage 接口
CompletionStage 接口实际上提供了同步或异步运行计算的舞台,所以我们可以通过实现多个 CompletionStage 命令,并且将这些命令串联在一起的方式实现多个命令之间的触发。
CompletionStage 方法清单
带有then通常是串行的表现
带有combine或者and 通常是聚合and
带有Either 通常是聚合or
带有handle或者exceptionally通常用来处理异常
快速开始
1. 创建实例
CompletableFuture completableFuture = new CompletableFuture<>();
System.out.println(completableFuture.get());
这个时候会发现,程序会一直处于阻塞状态,因为get()方法在任务结束之前将一直处在阻塞状态。这时需要我们调用complete()方法去手动完成。
CompletableFuture completableFuture = new CompletableFuture<>();
completableFuture.complete("hello");
System.out.println(completableFuture.get());
2. runAsync
静态方法。使用runAsnc进行一次异步计算
CompletableFuture hello = CompletableFuture.runAsync(() -> System.out.println("运行在一个单独的线程当中"));
hello.get();
runAsync里面传入的runnable是异步去执行,当我们没有传入Executor时,它会使用默认的连接池。
3. supplyAsync
静态方法。supplyAsync 是带有返回值的
CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> "hello");
System.out.println(completableFuture.get()); //hello
4. thenAccept
执行thenAccept之后,get()就不会再返回数据了
CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> "hello")
.thenAccept(System.out::println);
System.out.println(completableFuture.get()); // null
5. thenApply
可使用thenApply方法转换CompletableFuture对象的值,有点像使用Stream的map方法。
CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> {
sleep3s();
return "hello";
})
.thenApply(e -> e + " world")
.thenAccept(System.out::println);
completableFuture.join();
get() 和 join()的区别在于,抛出异常不同,join不强制你去处理异常。
6. thenCompose
thenCompose 的功能类似stream的flatMap,转换CompletableFuture,与thenApply不同的是,thenCompose接收的Function参数返回值必须是CompletionStage的实现类,更多用于连接CompletableFuture
public static void main(String[] args) {
CompletableFuture userIdFuture = getUserId("5466685299237");
CompletableFuture deptFuture = userIdFuture.thenCompose(Demo6::getDept);
System.out.println(deptFuture.join()); // 研发部
}
// 获取用户ID
static CompletableFuture getUserId(String code) {
return CompletableFuture.supplyAsync(() -> "10086");
}
// 获取用户所在部门信息
static CompletableFuture getDept(String userId) {
return CompletableFuture.supplyAsync(() -> "研发部");
}
7. thenCombine
聚合两个CompletableFuture
CompletableFuture helloFuture = CompletableFuture.supplyAsync(() -> "hello");
CompletableFuture worldFuture = CompletableFuture.supplyAsync(() -> "world");
helloFuture.thenCombine(worldFuture, (e1, e2)-> e1.toUpperCase() +" "+ e2.toUpperCase() + "!")
.thenAccept(System.out::println)
.join(); //HELLO WORLD!
8. allOf | anyOf
静态方法。顾名思义,allOf 所有都完成,anyOf只有有任意一个完成。
CompletableFuture task1 = CompletableFuture.supplyAsync(() -> {
sleep(3);
System.out.println("task1-完成");
return "task1";
});
CompletableFuture task2 = CompletableFuture.supplyAsync(() -> {
sleep(1);
System.out.println("task2-完成");
return "task3";
});
CompletableFuture task3 = CompletableFuture.supplyAsync(() -> {
System.out.println("task3-完成");
return "task3";
});
CompletableFuture.allOf(task1, task2, task3)
.thenAccept(e-> System.out.println("全部完成"))
.join();
上面代码如果换成anyOf,那么只要有一个完成,就会来到thenAccept
9. exceptionally
exceptionally 就相当于 catch,出现异常,将会跳过 thenApply 的后续操作,直接捕获异常,进行一场处理
int i = 1;
CompletableFuture exceptionally = CompletableFuture.supplyAsync(() -> {
if (i == 1)
throw new RuntimeException("异常啦~");
return "hello";
}).thenApply(e -> e.toUpperCase())
.exceptionally(ex -> "error");
System.out.println(exceptionally.join()); // error
10. handle
handle,的处理类似try...finally, 不管有没有发生异常都会来到handle。handle接收两个参数,一个正常的返回值,一个是异常。
int i = 1;
CompletableFuture exceptionally = CompletableFuture.supplyAsync(() -> {
if (i == 1)
throw new RuntimeException("异常啦~");
return "hello";
}).thenApply(e -> e.toUpperCase())
.handle((res, ex)-> {
return res;
});
System.out.println(exceptionally.join()); // null
总结
CompletableFuture给并发编程带来了新的体验,函数式编程,并行,聚合,异常等操作降低了并发编程难度。