concurrent.CompletableFuture处理多线程多阶段业务场景

package com.example.demo;

import lombok.SneakyThrows;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

/**
 * concurrent并发包CompletableFuture API
 *
 * @author majun
 * @version 1.0
 * @since 2023-08-12 21:05
 */
public class Test {
    public static final int MAX_SCORE = 100;
    public static final int INIT_SCORE = 0;


    /**
     * 场景:固定人数,且人数较少的考试,输出最高分
     *
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void threeStudentsKaoShi(){
        Integer res = CompletableFuture.completedFuture(INIT_SCORE).thenApplyAsync(score -> kaoShi(1, "语文")).thenApply(score -> kaoShi(1, "数学"))
                .thenCombine(CompletableFuture.completedFuture(INIT_SCORE).thenApplyAsync(score -> kaoShi(2, "语文")).thenApply(score -> kaoShi(2, "数学")), Integer::max)
                .thenCombine(CompletableFuture.completedFuture(INIT_SCORE).thenApplyAsync(score -> kaoShi(3, "语文")).thenApply(score -> kaoShi(3, "数学")), Integer::max)
                .exceptionally(throwable -> {
                    System.out.println(throwable.getMessage());
                    return 0;
                }).join(); // 类似同thread#join,main线程会等待子线程完成,这里也可以调用get阻塞方法来获取最终结果
        System.out.println(res);

    }

    /**
     * 场景:随机人数,输出最高分
     *
     * @throws ExecutionException
     * @throws InterruptedException
     */
    static void randomNumStudentsKaoShi() throws ExecutionException, InterruptedException {
        // 随机n各学生考试
        CompletableFuture[] array = Stream.iterate(0, n -> n + 1)
                .limit(getRandomInt())
                .map(id -> CompletableFuture.completedFuture(INIT_SCORE)
                        .thenApplyAsync(score -> kaoShi(id, "语文")).thenApply(score -> kaoShi(id, "数学")))
                .toArray(CompletableFuture[]::new);
        System.out.println("考试人数:" + array.length);
        // 考试结果合计最高分:whenComplete 上一步CompletableFuture.allOf返回的是泛型是Void类型,unused始终是null,异步运行结果只能从CompletableFuture[]遍历获取
        CompletableFuture<Void> future = CompletableFuture.allOf(array).whenComplete((unused, throwable) -> {
            if (throwable == null) {
                Integer maxScore = Arrays.stream(array).map(item -> {
                    try {
                        return (int) item.get();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return 0;
                }).max(Integer::compareTo).get();
                System.out.println(maxScore);
            }
        });
        future.get();


    }


    /**
     * 考试
     *
     * @param studentId
     * @param subject   科目
     * @return 得分
     */
    @SneakyThrows
    private static int kaoShi(int studentId, String subject) {
        System.out.println(studentId + "开始考试" + subject);

            int timeCost = getRandomInt();
            TimeUnit.SECONDS.sleep((int) (timeCost));
            System.out.println(studentId + subject + "考试耗时" + timeCost);

        return MAX_SCORE - getRandomInt();
    }

    /**
     * 1-10随机数
     *
     * @return
     */
    private static int getRandomInt() {
        return (int) (Math.random() * 10 + 1);
    }

    @SneakyThrows
    public static void main(String[] args) {
        System.out.println(LocalDateTime.now());
        //threeStudentsKaoShi();
        randomNumStudentsKaoShi();
        System.out.println(LocalDateTime.now());
    }

}

总结:

  • 该API适合多线程多阶段业务场景,可以很方便的设定下一步任务、同步/异步,使用CountdownLaunch/CircleBarrier+多线程也可以实现
  • thenApplyAsync API默认是基于Main线程拉起子线程异步执行,也可以传入线程池
  • spring ListenableFuture是基于concurrent.CompletableFuture实现的,但该API的回调方法即终结了,不便添加后续Stage。

你可能感兴趣的:(java,多线程,回调)