你能列举CompletableFuture的几种用法吗?
对CompletableFuture类,是不是既熟悉又陌生?
熟悉的是项目里经常用到,陌生的是那些都是别人写的,直到某个项目打包启动后,既不运行也没有异常爆出?!尴尬了,赶紧研究下吧!虽然最开始并没有怀疑是它的问题,可是最后问题就定位在这里。
CompletableFuture是java.util.concurrent并发线程工具包中在java8新增特性,从字面意思看是可完成的Future,可以主动设置返回值和状态,其支持流式计算、函数式编程、完成通知、自定义异常处理等新特性。现在看看最常规的场景及使用。
第一,创建一个异步任务,获取异步任务的执行结果;
// ForkJoin的线程池来执行被提交的任务
CompletableFuture future = CompletableFuture.supplyAsync(()->{
System.out.println("compute test");
return "test";
});
// 通过get或join获取最终返回结果
String result = future.join();
System.out.println("get result: " + result);
执行结果:
compute test
get result: test
supplyAsync有两个参数,执行的任务和线程池,这里使用默认的线程池,默认的线程池是ForkJoinPool类。
默认的线程池:
第二,先执行第一个任务再执行第二个任务 ****thenApply****;
// 先执行第一个任务
CompletableFuture future1 = CompletableFuture.supplyAsync(()->{
System.out.println("compute 1");
// 执行完毕后,返回一个值
return 1;
});
// 传入第一个任务的执行结果(第一个任务必须执行完毕),执行第二个任务
// thenApply(有返回值,有入参) / thenAccept(无返回值,有入参) / thenRun(无返回值,无入参)
CompletableFuture future2 = future1.thenApply((p)->{
System.out.println("compute 2");
// 执行完毕后,返回两个执行结果之和
return p+10;
});
System.out.println("result: " + future2.join());
运行结果:
compute 1
compute 2
result: 11
说明:两个任务中有依赖关系。
thenApply能接收上一个任务的返回值(有入参),并且可以return;
除了thenApply,还有thenAccept和thenRun。
三种方法的区别:thenApply(有返回值,有入参) ;
thenAccept(无返回值,有入参) ,
thenRun(无返回值,无入参)。
thenCompose把两个任务组合:
CompletableFuture future1 = CompletableFuture.supplyAsync(()->{
System.out.println("compute 1");
return 1;
});
// 任务1组合任务2
CompletableFuture future2 = future1.thenCompose((r)->CompletableFuture.supplyAsync(()->r+10));
System.out.println(future2.join());
至于thenApply和thenCompose有什么区别,需要在实战中才能摸索出来,从一个简易的demo是看不出来什么的。
第三,****thenCombine****合并两个任务的执行结果;
CompletableFuture future1 = CompletableFuture.supplyAsync(()->{
System.out.println("compute 1");
return 1;
});
// 异步执行任务2
CompletableFuture future2 = CompletableFuture.supplyAsync(()->{
System.out.println("compute 2");
return 10;
});
// 将执行结果合并
CompletableFuture future3 = future1.thenCombine(future2, (r1, r2)->r1 + r2);
System.out.println("result: " + future3.join());
运行结果:
compute 1
compute 2
result: 11
说明:两个任务中没有依赖关系,但需要等待两个任务都执行完毕。
第四,任务完成时的回调通知****,whenComplete或handle****;
CompletableFuture future1 = CompletableFuture.supplyAsync(()->{
System.out.println("compute 1");
return 1;
});
CompletableFuture future2 = future1.whenComplete((r, e)->{
if(e != null){
System.out.println("compute failed!");
} else {
System.out.println("received result is " + r);
}
});
System.out.println("result: " + future2.join());
运行结果:
compute 1
received result is 1
result: 1
想要future2也有返回值,那就使用handler吧;
CompletableFuture future1 = CompletableFuture.supplyAsync(()->{
System.out.println("compute 1");
return 1;
});
CompletableFuture future2 = future1.handle((r, e)->{
if(e != null){
System.out.println("compute failed!");
return r;
} else {
System.out.println("received result is " + r);
return r + 10;
}
});
System.out.println("result: " + future2.join());
handler可以return,whenComplete无返回值;
第五****,****两个任务完成一个就返回,applyToEitherAsync 或 anyOf;
CompletableFuture future1 = CompletableFuture.supplyAsync(()->{
System.out.println("compute 1");
return 1;
});
CompletableFuture future2 = future1.handle((r, e)->{
if(e != null){
System.out.println("compute failed!");
return r;
} else {
System.out.println("received result is " + r);
return r + 10;
}
});
System.out.println("result: " + future2.join());
运行结果:
任务1
任务3,接收的结果:1
最终结果:1
说明:这里可以把两个任务的执行时间进行调整,这样就可以得到不同的运行结果。
如果任务不止两个,试试anyOf吧。
// anyOf,多个任务中,完成一个就返回
CompletableFuture future01 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
System.out.println("任务1");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "1";
});
CompletableFuture future02 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
System.out.println("任务2");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "2";
});
CompletableFuture future03 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
System.out.println("任务3");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "3";
});
CompletableFuture
第六,allof 等待所有任务都执行完;
// allof等待所有任务完成
CompletableFuture future01 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
System.out.println("任务1");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "1";
});
CompletableFuture future02 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
System.out.println("任务2");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "2";
});
CompletableFuture future03 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
System.out.println("任务3");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "3";
});
CompletableFuture allOf = CompletableFuture.allOf(future01, future02, future03);
//获取异步任务的返回值
try {
allOf.get();//等待所有结果完成
System.out.println("返回结果:" + future01.get() + future02.get() + future03.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
运行结果:
任务2
任务3
任务1
返回结果:123
第七****,exceptionally 捕获****执行****中的错误;
// 7. 在使用supplyAsync异步调用时,出现异常打印日志中看不到,可以通过exceptionally处理
CompletableFuture.supplyAsync(()->{
try{
String[] aa = {"aa","bb"};
System.out.println(aa[2]);
}catch(Exception e){
throw new RuntimeException("出现异常了");
}
return 1;
}).whenComplete((r, e)->{
if(e != null){
//第一个任务失败了
} else {
//第一个任务成功后做什么
}
}).exceptionally(e->{
CompletionException ex = new CompletionException(e);
System.out.println(ex);
throw ex;
});
运行结果:
java.util.concurrent.CompletionException: java.util.concurrent.CompletionException: java.lang.RuntimeException: 出现异常了
任务执行过程中,出现错误,无法打印在日志里,将是一件很糟糕的事情。
最后总结
1.异步任务的建立supplyAsync,获得任务的执行结果get()或join();
2.thenApply、thenAccept、thenRun传入参数和返回值的差异,可以根据需要使用;
3.thenApply、thenCompose、thenCombine,根据任务间的关系使用对应关系的方法;
4.exceptionally及时捕捉异常的业务逻辑,以免找不到错误的根源;
5.如同2,很多方法方法名相似,用法上略有差异,这个还需要在实战中摸索。
参考文档:
https://blog.csdn.net/tongtest/article/details/107549749https://blog.csdn.net/weixin_45952781/article/details/120091810