01-CompletableFuture异步线程 入门

前言

emmm, 有好长一段时间没有发布新的文章了,最后一篇发布还是1月21日,到现在已经快3个月了,这段时间我去干嘛了呢?

1: 学习数据结构与算法, 但是还没有学完,打算等学习完毕后再给大家分享

2: 学习Java 9 - 15的新特性, 应为关注Java的发展方向这一块来说,基本是每个学习Java的人员所必备的, 为啥没有16和17呢?应为我在网上没有找到好的视频,所以等以后找到了在学习而且新特特性应该不是很多,但是9-17加起来就很多了

3: 就是最近这段时间我离职了, 在交接和找新的工作,所很大一部分精力都用在了这个上面

但是今天给大家带来了一个新的东西,这个东西用的人也比较少, 其实也不是很新,是Java8中的一个类而已,但是非常好用, 用过多线程的人应该都知道,线程的创建,回收,管理,池化,都很难弄, 但是学习了这个东西, 妈妈再也不用担心我用不好多线程了

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 {

    @Test
    public 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)返回

第一个任务的需求点在于线程的开启

第二个任务的需求点在于两个线程的连接

第三个任务的需求点在于两个线程的结果合并

怎么样到这里是不是已经简单的入门了呢

作者:彼岸舞

时间:2022\04\11

内容关于:CompeletableFuture

本文来源于网络,只做技术分享,一概不负任何责任

posted @ 2022-04-11 18:05  彼岸舞  阅读(88)  评论(0编辑  收藏  举报