Jdk21优雅处理异步任务

摘要:本文主要介绍jdk21 如何优雅的处理异步执行任务;下面给出了几个常见的案例。

CompletableFuture解决回调地狱写法

顺序执行(前阶段结果传递给后阶段)

适用于需要按顺序执行异步操作的场景(如:先查用户,再查订单)。

方法 描述
thenApply(Function) 同步处理前阶段结果,返回新结果(有入参、有返回值)。
thenAccept(Consumer) 同步消费前阶段结果,无返回值(有入参、无返回值)。
thenRun(Runnable) 前阶段完成后执行操作,不依赖结果(无入参、无返回值)。
thenCompose(Function) 合并两个阶段为链式调用(前阶段返回CompletableFuture,直接衔接后阶段)。

案例

csharp 复制代码
    public void test01(){
        CompletableFuture<OrderDto> orderDtoCompletableFuture = CompletableFuture.supplyAsync(() -> {
            OrderDto orderDto = new OrderDto();
            System.out.println(Thread.currentThread().getName());
            orderDto.setId(1L);
            return orderDto;
        }).thenApplyAsync(orderDto -> {
            try{
                Thread.sleep(2000L);
            }catch (Exception ex){

            }
            orderDto.setName("测试");
            System.out.println("模拟执行耗时,查询商品信息");
            return orderDto;
        }).thenApplyAsync(orderDto -> {
            try{
                Thread.sleep(2000L);
            }catch (Exception ex){

            }
            orderDto.setMoney(new BigDecimal("20"));
            System.out.println("模拟执行耗时,计算商品总价");
            return orderDto;
        }).exceptionally(ex -> {
            log.error("执行异常", ex);
            return null;
        });
        OrderDto or = orderDtoCompletableFuture.join();
        System.out.println(or);
        System.out.println("主线程执行完了");
    }

并行执行(多阶段独立执行后合并)

适用于并行处理多个任务后合并结果的场景(如:同时计算用户积分和统计订单)。

方法 描述
thenCombine(CompletableFuture, BiFunction) 等待两个阶段都完成,合并结果(前阶段结果 + 后阶段结果 → 新结果)。
allOf(CompletableFuture...) 等待所有阶段完成(无返回值,需手动收集结果)。
anyOf(CompletableFuture...) 等待任意一个阶段完成(返回第一个完成的结果)。

案例

ini 复制代码
// 阶段 1:计算用户积分(异步)
CompletableFuture<Integer> pointsFuture = CompletableFuture.supplyAsync(() -> 
    calculatePoints(user)
);

// 阶段 2:统计有效订单数(异步)
CompletableFuture<Integer> validOrdersFuture = CompletableFuture.supplyAsync(() -> 
    countValidOrders(user)
);

// 合并两个阶段的结果(总积分 = 积分 + 订单数×10)
CompletableFuture<Integer> totalFuture = pointsFuture.thenCombine(
    validOrdersFuture, 
    (points, orders) -> points + orders * 10
);

int total = totalFuture.join(); // 输出总结果

异常处理

链式调用中若某阶段抛出异常,后续阶段会被静默取消,需通过以下方法捕获异常:

方法 描述
exceptionally(Function) 异常时返回默认值(类似 try-catch 的 catch 块)。
handle(BiFunction) 无论成功或失败都处理(可返回新结果或默认值)。

案例

arduino 复制代码
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    if (condition) throw new RuntimeException("查询失败");
    return "正常数据";
}).exceptionally(ex -> {
    System.out.println("异常处理:" + ex.getMessage());
    return "默认数据"; // 异常时返回默认值
});

String result = future.join(); // 若异常,返回 "默认数据"

虚线程使用案例

虚线程异步执行并等待结果

ini 复制代码
public class VirtualThreadCompletableFutureDemo {
    public static void main(String[] args) {
        ExecutorService virtualExecutor = Executors.newVirtualThreadPerTaskExecutor();
        List<CompletableFuture<Void>> futures = new ArrayList<>();

        // 提交 3 个虚线程任务
        for (int i = 0; i < 3; i++) {
            int taskId = i;
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                try {
                    Thread.sleep(1000); // 模拟任务执行
                    System.out.println("任务 " + taskId + " 完成");
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }, virtualExecutor);
            futures.add(future);
        }

        // 判断所有任务是否完成(阻塞等待)
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

        System.out.println("所有任务完成!");
        virtualExecutor.shutdown();
    }
}

结果

复制代码
任务 0 完成
任务 1 完成
任务 2 完成
所有任务完成!

扩展:单个任务异常判断

arduino 复制代码
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    throw new RuntimeException("任务失败"); // 模拟异常
}, virtualExecutor);

future.whenComplete((result, ex) -> {
    if (ex != null) {
        System.out.println("任务异常:" + ex.getMessage()); // 输出:任务异常:任务失败
    }
});

// 主动检查是否异常完成
if (future.isCompletedExceptionally()) {
    System.out.println("任务因异常终止");
}
相关推荐
Lhappy嘻嘻3 小时前
Java IO|File 文件操作 + 字节流 / 字符流完整笔记 + 递归删除文件实战
java·笔记·php
To_OC3 小时前
手写 AI 编程 Agent 的命令执行工具:我被 child_process 坑出来的实战经验
后端·node.js·agent
herosunly3 小时前
60ms 不是启动快,而是不用重新启动:CubeSandbox 极速冷启动架构拆解
架构·cube sandbox
伊玛目的门徒3 小时前
试用leetcode之典中典 二数之和问题
java·算法·leetcode
zlinear数据采集卡4 小时前
从气动比例阀到任意波形:硬核拆解ZLinear采集卡的DAC输出架构与工业闭环控制实战
arm开发·架构
逝水无殇5 小时前
C# 异常处理详解
开发语言·后端·c#
懒鸟一枚6 小时前
深入理解 Linux 内存、Swap 交换分区与分页机制的关系
java·linux·数据库
2601_961946086 小时前
AI API 网关实战:从单 Key 管理到企业级多租户架构
大数据·人工智能·金融·架构·api·个人开发
我命由我123457 小时前
执行 Gradle 指令报错,无法将“grep”项识别为 cmdlet、函数、脚本文件或可运行程序的名称
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
考虑考虑8 小时前
Sentinel安装
java·后端·微服务