CompletableFuture是什么
从名称看来和Future有关,没错,他也是Future的实现,和FutureTask平级,也是用来实现异步线程任务的,并且携带返回值, 具体的使用直接从需求出发,关注下面的需求和实现, 即可掌握
需求
小白来餐厅吃饭, 点了一盘番茄炒蛋+米饭,小白开始打王者,厨师开始炒菜,小白开吃
需求点: 厨师需要单独的线程
实现
编写代码
package com.dance;import org.junit.jupiter.api.Test;import java.util.StringJoiner;import java.util.concurrent.CompletableFuture;public class CompletableFutureTest {@Testpublic void testOne(){print("小白进入餐厅");print("小白点了 番茄炒蛋 + 一碗米饭");CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {print("厨师炒菜");sleep(200);print("厨师打饭");sleep(100);return "番茄炒蛋 + 米饭 好了";});print("小白在打王者");print(String.format("%s , 小白开吃", cf1.join()));}/*** 休眠方法* @param millis 毫秒*/public static void sleep(long millis){try {Thread.sleep(millis);} catch (InterruptedException e) {e.printStackTrace();}}/*** 打印方法* @param text 文本*/public static void print(String text){String str = new StringJoiner("\t|\t").add(String.valueOf(System.currentTimeMillis())).add(String.valueOf(Thread.currentThread().getId())).add(Thread.currentThread().getName()).add(text).toString();System.out.println(str);}}
执行结果
1649430128924 | 1 | main | 小白进入餐厅1649430128924 | 1 | main | 小白点了 番茄炒蛋 + 一碗米饭1649430128926 | 1 | main | 小白在打王者1649430128927 | 24 | ForkJoinPool.commonPool-worker-19 | 厨师炒菜1649430129134 | 24 | ForkJoinPool.commonPool-worker-19 | 厨师打饭1649430129244 | 1 | main | 番茄炒蛋 + 米饭 好了 , 小白开吃
需求进化
在餐厅中一般厨师都只负责炒菜,像打饭这样的事情都是交给服务员来的
需求点:厨师炒完菜后交给服务员,服务员新开线程去打饭
实现
编写代码
@Test
public void testTwo(){
    print("小白进入餐厅");
    print("小白点了 番茄炒蛋 + 一碗米饭");
    CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
        print("厨师炒菜");
        sleep(200);
        return "番茄炒蛋";
    }).thenCompose(dis -> CompletableFuture.supplyAsync(() -> {
        print("服务员打饭");
        sleep(100);
        return dis + " + 米饭 好了";
    }));
    print("小白在打王者");
    print(String.format("%s , 小白开吃", cf1.join()));
}
执行结果
1649431094323    |    1    |    main    |    小白进入餐厅
1649431094324    |    1    |    main    |    小白点了 番茄炒蛋 + 一碗米饭
1649431094326    |    1    |    main    |    小白在打王者
1649431094326    |    24    |    ForkJoinPool.commonPool-worker-19    |    厨师炒菜
1649431094538    |    24    |    ForkJoinPool.commonPool-worker-19    |    服务员打饭
1649431094645    |    1    |    main    |    番茄炒蛋 + 米饭 好了 , 小白开吃
按道理来说这里应该是两个线程的,但是估计我这个执行的太快了,所以后面的任务也提交给了这个线程,我感觉这种代码调用流程就很清晰,看着像Promise
需求进化
小白进入餐厅的时候,开始点菜,要一盘番茄炒蛋+米饭, 但是这个时候米饭是没有蒸好的,需要开始去蒸,所以厨师炒菜,服务员去蒸饭,这两个事情应该是同时进行的,在厨师炒完菜,服务员蒸好饭,厨师将菜交给服务员,服务员打饭,交给小白,小白吃饭
需求点: 厨师炒菜和服务员蒸饭需要同时进行,并且是厨师炒完菜交给服务员
实现
编写代码
@Test
public void testThree(){
    print("小白进入餐厅");
    print("小白点了 番茄炒蛋 + 一碗米饭");
    CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
        print("厨师炒菜");
        sleep(200);
        return "番茄炒蛋";
    }).thenCombine(CompletableFuture.supplyAsync(() -> {
        print("服务员开始蒸饭");
        sleep(300);
        return "米饭";
    }), (dis, rice) -> {
        print("服务员打饭");
        sleep(100);
        return String.format("%s + %s , 好了", dis, rice);
    });
    print("小白在打王者");
    print(String.format("%s , 小白开吃", cf1.join()));
}
执行结果
1649431885256    |    1    |    main    |    小白进入餐厅
1649431885256    |    1    |    main    |    小白点了 番茄炒蛋 + 一碗米饭
1649431885259    |    1    |    main    |    小白在打王者
1649431885259    |    24    |    ForkJoinPool.commonPool-worker-19    |    服务员开始蒸饭
1649431885259    |    25    |    ForkJoinPool.commonPool-worker-5    |    厨师炒菜
1649431885571    |    24    |    ForkJoinPool.commonPool-worker-19    |    服务员打饭
1649431885681    |    1    |    main    |    番茄炒蛋 + 米饭 , 好了 , 小白开吃
总结
| 方法名 | 描述 | 
|---|---|
| supplyAsync | 用来开启一个异步任务 | 
| thenCompose | 用来连接两个有依赖关系的任务,结果由最后一个返回 | 
| thenCombine | 用来合并两个任务,结果由函数(BiFunction)返回 | 

第一个任务的需求点在于线程的开启
第二个任务的需求点在于两个线程的连接
第三个任务的需求点在于两个线程的结果合并
怎么样到这里是不是已经简单的入门了呢
