JDK8-11-CompletableFuture(7)- thenApply,thenApplyAsync方法使用
thenApply 使用
回到顾客点餐,厨师炒菜打饭的例子:
1.顾客进入餐厅
2.顾客开始点餐
3.厨师按照顾客所选菜品开始炒菜
4.厨师打饭(假设没有服务员)
5.顾客开始吃饭
其中,在厨师准备饭菜的3,4步骤中,顾客处于等待状态,可以做些其他事情,比如玩手机,下面用程序模拟这个案例:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class SupplyAsyncTest06 {
public static void main(String[] args) {
PrintTool.printTimeAndThread("顾客进入餐厅。。。");
PrintTool.printTimeAndThread("顾客开始点餐。。。");
CompletableFuture<String> chefCF = CompletableFuture.supplyAsync(() -> {
PrintTool.printTimeAndThread("厨师开始炒菜。。。");
PrintTool.sleep(500);
return "番茄炒蛋";
}).thenApply(dish -> {
PrintTool.printTimeAndThread("厨师开始盛饭。。。");
PrintTool.sleep(100);
return dish + "+米饭";
});
PrintTool.printTimeAndThread("顾客玩手机中。。。");
try {
PrintTool.printTimeAndThread(String.format("顾客开始吃%s", chefCF.get()));
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
运行结果:
1666405340730 | 1 | main | 顾客进入餐厅。。。
1666405340733 | 1 | main | 顾客开始点餐。。。
1666405340879 | 14 | ForkJoinPool.commonPool-worker-1 | 厨师开始炒菜。。。
1666405340879 | 1 | main | 顾客玩手机中。。。
1666405341388 | 14 | ForkJoinPool.commonPool-worker-1 | 厨师开始盛饭。。。
1666405341417 | 1 | main | 顾客开始吃番茄炒蛋+米饭
thenApply 与 thenCompose 区别
thenApply 表示任务执行完后再执行某个操作(任务的后置处理),与 thenCompose 类似,区别在于:
① thenApply 接收参数为 Function<? super T,? extends U> fn,函数式方法返回泛型对象U,thenCompose 接收参数为 Function<? super T, ? extends CompletionStage<U>> fn ,函数式方法返回
CompletionStage<U>
② thenApply 将方法内的操作当成上一个任务的继续,可以看成是同一个任务,所以在同一个线程执行,thenCompose 将子任务放到 ForkJoinPool.commonPool 线程池中执行,与父任务有可能处于同一线程,也可能不是同一线程
thenApplyAsync 使用
以上述例子为分析依据,将厨师打饭换成服务员A打饭:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class SupplyAsyncTest07 {
public static void main(String[] args) {
PrintTool.printTimeAndThread("顾客进入餐厅。。。");
PrintTool.printTimeAndThread("顾客开始点餐。。。");
CompletableFuture<String> chefCF = CompletableFuture.supplyAsync(() -> {
PrintTool.printTimeAndThread("厨师开始炒菜。。。");
PrintTool.sleep(500);
return "番茄炒蛋";
}).thenApplyAsync(dish -> {
PrintTool.printTimeAndThread("服务员A开始盛饭。。。");
PrintTool.sleep(100);
return dish + "+米饭";
});
PrintTool.printTimeAndThread("顾客玩手机中。。。");
try {
PrintTool.printTimeAndThread(String.format("顾客开始吃%s", chefCF.get()));
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
执行结果如下:
1666498382154 | 1 | main | 顾客进入餐厅。。。
1666498382154 | 1 | main | 顾客开始点餐。。。
1666498382295 | 14 | ForkJoinPool.commonPool-worker-1 | 厨师开始炒菜。。。
1666498382295 | 1 | main | 顾客玩手机中。。。
1666498382802 | 14 | ForkJoinPool.commonPool-worker-1 | 服务员A开始盛饭。。。
1666498382939 | 1 | main | 顾客开始吃番茄炒蛋+米饭
thenApplyAsync 会将函数式方法中的操作放到一个新的任务中执行,也就是 服务员A盛饭会在一个新的任务中执行,但是上述例子厨师炒菜和服务员打饭都是由 ForkJoinPool.commonPool-worker-1 线程执行的,原因在于,thenApplyAsync 会等待调用任务执行完再执行本方法内的操作,也就是 服务员A开始盛饭时,厨师已经炒完菜了,所以 ForkJoinPool.commonPool-worker-1 处于空闲状态,虽然 服务员A打饭是一个新的任务,但是都是由 ForkJoinPool.commonPool 线程池分配线程执行,所以有可能被处于空闲状态的 ForkJoinPool.commonPool-worker-1 执行,这里要将任务与线程的概念区分开