CompletableFuture的用法有哪些?

你能列举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 anyOf = CompletableFuture.anyOf(future01, future02, future03);
//获取异步任务的返回值
try {
System.out.println("返回结果:" + anyOf.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
 
 

第六,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

你可能感兴趣的:(CompletableFuture的用法有哪些?)