文章目录
一.多阶段异步任务
在我们的实际生产过程中,可能会遇到这种场景:
我们需要有多个异步任务,并且这些异步任务是相互依赖的,比如说,现在有三个异步任务,任务2需要使用到任务1的计算结果,任务3又需要用到任务2的计算结果,那应该如何去处理呢?使用CompletableFuture就非常简单了.
二.代码演示
1.thenApply
1.该方法可以将异步任务串行化
2.该方法对于异常的处理是:
当某一步出现异常时,直接执行异常处理和回调函数,不进行后面异步任务的处理
举例:
当没有异常时:
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() -> {
System.out.println("第一个异步任务执行完毕...");
return 1;
},executorService).thenApply(result -> {
System.out.println("第二个异步任务执行完毕...");
return result+1;
}).thenApply(result -> {
System.out.println("第三个异步任务执行完毕...");
return result+1;
}).whenComplete((result,e) -> {
System.out.println("异步任务执行完毕,已执行回调函数.最终得结果为:"+result);
}).exceptionally(e -> {
e.printStackTrace();
System.out.println(e.getCause());
return null;
});
executorService.shutdown();
输出结果:
当出现异常时:
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() -> {
System.out.println("第一个异步任务执行完毕...");
return 1;
},executorService).thenApply(result -> {
//制造异常
int i=2/0;
System.out.println("第二个异步任务执行完毕...");
return result+1;
}).thenApply(result -> {
System.out.println("第三个异步任务执行完毕...");
return result+1;
}).whenComplete((result,e) -> {
System.out.println("异步任务执行完毕,已执行回调函数.最终得结果为:"+result);
}).exceptionally(e -> {
System.out.println("程序出现异常...");
System.out.println(e.getCause());
e.printStackTrace();
return null;
});
executorService.shutdown();
输出结果:
2.handle
该方法和thenApply差不多,区别是对异常得处理
当出现异常时,可以根据所带得异常参数进行处理,后面得异步任务也可以接着执行
举例:
无异常得情况和thenApply是一样得,这里只举例有异常得情况
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() -> {
System.out.println("第一个异步任务执行完毕...");
return 1;
},executorService).handle((result,e) -> {
//制造异常
int i=2/0;
System.out.println("第二个异步任务执行完毕...");
return result+1;
}).handle((result,e) -> {
System.out.println("第三个异步任务执行完毕...");
return result+1;
}).whenComplete((result,e) -> {
System.out.println("异步任务执行完毕,已执行回调函数.最终得结果为:"+result);
}).exceptionally(e -> {
System.out.println("程序出现异常...");
System.out.println(e.getCause());
e.printStackTrace();
return null;
});
executorService.shutdown();
输出结果:
可以看到这里虽然第二个异常任务出现异常没有执行完毕,但仍然会执行第三个异步任务.
3.thenAccept.
thenAccept和thenApply也差不多,会新起一个异步任务,并且依赖前一个异步任务得结果,区别是:
thenAccept方法不需要返回值
并且因为thenAccept方法没有返回值,在使用thenAccept方法后调用回调函数时,返回得结果是null.
举例:
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() -> {
System.out.println("第一个异步任务执行完毕...");
System.out.println("===========================================");
return 1;
},executorService).thenAccept((result) -> {
System.out.println("第二个异步任务开始执行...");
System.out.println("接收到第一个异步任务得结果:"+result);
System.out.println("第二个异步任务执行完毕...");
System.out.println("===========================================");
}).whenComplete((result,e) -> {
System.out.println("异步任务执行完毕,已执行回调函数.最终得结果为:"+result);
}).exceptionally(e -> {
System.out.println("程序出现异常...");
System.out.println(e.getCause());
e.printStackTrace();
return null;
});
executorService.shutdown();
输出结果:
三.thenApply、handle等和对应Async方法得区别
之前说明了thenApply,handle,thenAccept方法得用法和区别,那其实这几个方法都有对应得Async方法.
那主要得区别是什么呢?其实主要是使用线程池得区别,这里以thenApply方法进行举例说明.其他两个和这个是一样得.
1.当没有指定线程池时
thenApply和thenApplyAsync是一样得,都是使用得默认得ForkJoinPool线程池.
CompletableFuture.supplyAsync(() -> {
System.out.println("第一个异步任务执行完毕...使用得线程为:"+Thread.currentThread().getName());
System.out.println("===========================================");
return 1;
}).thenApply((result) -> {
System.out.println("第二个异步任务执行完毕...");
System.out.println("使用得线程为:"+Thread.currentThread().getName());
System.out.println("使用方法为:thenApply");
System.out.println("===========================================");
return result+1;
}).thenApplyAsync(result -> {
System.out.println("第三个异步任务执行完毕...");
System.out.println("使用得线程为:"+Thread.currentThread().getName());
System.out.println("使用方法为:thenApplyAsync");
System.out.println("===========================================");
return result+1;
}).whenComplete((result,e) -> {
System.out.println("所有异步任务执行完毕.");
}).exceptionally(e -> {
System.out.println("程序出现异常...");
System.out.println(e.getCause());
e.printStackTrace();
return null;
});
输出结果:
2.当使用指定线程池时
thenApply会使用指定线程池,而thenApplyAsync会使用ForkJoinPool线程池.
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() -> {
System.out.println("第一个异步任务执行完毕...使用得线程为:"+Thread.currentThread().getName());
System.out.println("===========================================");
return 1;
},executorService).thenApply((result) -> {
System.out.println("第二个异步任务执行完毕...");
System.out.println("使用得线程为:"+Thread.currentThread().getName());
System.out.println("使用方法为:thenApply");
System.out.println("===========================================");
return result+1;
}).thenApplyAsync(result -> {
System.out.println("第三个异步任务执行完毕...");
System.out.println("使用得线程为:"+Thread.currentThread().getName());
System.out.println("使用方法为:thenApplyAsync");
System.out.println("===========================================");
return result+1;
}).whenComplete((result,e) -> {
System.out.println("所有异步任务执行完毕.");
}).exceptionally(e -> {
System.out.println("程序出现异常...");
System.out.println(e.getCause());
e.printStackTrace();
return null;
});
executorService.shutdown();
输出结果:
3.源码分析
可以看到thenApplyAsync会使用一个默认得线程池.跟着继续往下走.
到这里就能很清楚得了解到区别了.