最近阅读了java 8实战和java 8 函数式编程两本书,并参照书中的例子进行了编程练习,代码放在了GitHub上。对stream流的串行,并行,和一些流的操作函数进行了实操。也都是比较容易上手的。不过对于CompletableFuture类的使用,书中对其方法和应用讲的较为简单,自己在实际应用中遇到好多新的需求,经常需要再查资料来解决,现在把它们记录下来,加深印象。
#何时使用CompletableFuture
- 进行计算密集型的操作,并且没有I/O,那么推荐使用Stream接口,因为实现简单,同时效率也可能是最高的(如果所有的线程都是计算密集型的,那就没有必要创建比处理器核数更多的线程)。
- 如果并行操作设计等到I/O的操作(网络连接,请求等),那么使用CompletableFuture灵活性更好,通过控制线程数量来优化程序的运行。
- CompletableFuture可以对流进行并行计算,但并行处理数量为计算机CPU数量,加入线程池可以并行加并发,提高效率。参见代码test3和test4
#简单的异步执行
##无返回值
CompletableFuture<Integer> future = CompletableFuture.runAsync(()->accountDao.update(account));
有返回值
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(()->accountDao.update(account));
以Async结尾的方法表示会另起一个线程执行,默认使用ForkJoinPool.commonPool(),也可以指定自定义线程池。
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
#得到异步处理结果,进一步计算
thenAccept
CompletableFuture<Void> voidFuture = CompletableFuture.supplyAsync(() -> accountDao.update(account))
.thenAccept(result -> {
if (result != 1) {
throw new RuntimeException("账户更新写入数据库失败");
}
});
纯消费,异步执行方法为thenAcceptAsync
thenApply
CompletableFuture<User> userFuture = CompletableFuture.supplyAsync(() -> accountDao.getAccountById(id))
.thenApply(account -> {
User user = new User();
user.setName = account.getName();
return new user;
});
该方法可以对返回future的类型进行转换
使用异步结果进行另一个异步操作
CompletableFuture<List<User>> userListFuture = CompletableFuture.supplyAsync(() -> accountDao.getAccountById(id))
.thenCompose(
account -> CompletableFuture.supplyAsync(() -> accountDao.getUserListByName(account.getName()))
);
该方法与thenApply都使用了上一个异步结果,区别是thenCompose返回一个新的future,而thenApply返回一个对象替换原future的泛型。
whenComplete响应结果或异常
方法声明为
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)
简单示例:
CompletableFuture<Integer> resultFuture = CompletableFuture.supplyAsync(() -> accountDao.update(account))
.whenComplete(result,ex) -> {
if(ex!=null){
ex.printStackTrace();
}
if (result != 1) {
throw new RuntimeException("账户更新写入数据库失败");
}
});
whenComplete不会改变原future的结果,还可以在其上调用其他方法。
handle 处理异常
CompletableFuture.supplyAsync(()->{throw new RuntimeException("test");}).handle((result,ex)->{
if (ex!=null){
return null;
}
return result;
});
与whenComplete不同的是,handle的c入参是BiFunction类型,可以对结果类型进行转换。
exceptionally 只处理异常
whenComplete和handle都是既响应正常完成也响应异常,如果只对异常感兴趣,可以使用exceptionally。其声明为:
public CompletableFuture<T> exceptionally(
Function<Throwable, ? extends T> fn)
后面还想写CompletableFuture对两个以及多个future处理,涉及到的方法为thenAcceptBoth、thenCombine、runAfterBoth、runAfterEither、applyToEither、acceptEither、allOf、anyOf。不过这篇博客讲的更为仔细,就不赘述了。