Java 8 CompletableFuture:异步编程的利器与最佳实践

news2025/4/16 13:59:13

目录

1. 创建异步任务

1.1 使用默认线程池

1.2 使用自定义线程池

2. 异步回调处理

2.1 thenApply 和 thenApplyAsync

2.2 thenAccept 和 thenAcceptAsync

2.3 thenRun 和 thenRunAsync

3. 异常处理

3.1 whenComplete 和 whenCompleteAsync

3.2 handle 和 handleAsync

4. 多任务组合

4.1 thenCombine 和 thenCombineAsync

4.2 thenAcceptBoth 和 thenAcceptBothAsync

4.3 runAfterBoth 和 runAfterBothAsync


以下是 CompletableFuture 的不同用法示例,包括注释、使用场景以及对比图:

1. 创建异步任务

1.1 使用默认线程池

// 使用 supplyAsync 创建一个有返回值的异步任务
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 模拟耗时任务
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "Hello, CompletableFuture!";
});
System.out.println(future.join()); // 阻塞等待任务完成并获取结果

使用场景:适用于简单的异步任务,不需要自定义线程池。

1.2 使用自定义线程池

ExecutorService customExecutor = Executors.newFixedThreadPool(4);

// 使用 supplyAsync 创建一个有返回值的异步任务,并指定自定义线程池
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 模拟耗时任务
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "Hello, CompletableFuture with custom executor!";
}, customExecutor);
System.out.println(future.join()); // 阻塞等待任务完成并获取结果

使用场景:适用于需要对线程池进行精细控制的场景,例如限制线程数量或使用特定的线程池策略。

2. 异步回调处理

2.1 thenApply 和 thenApplyAsync

// 使用默认线程池
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello")
    .thenApply(s -> s + " World"); // 同步回调
System.out.println(future1.join());

// 使用自定义线程池
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Hello", customExecutor)
    .thenApplyAsync(s -> s + " World", customExecutor); // 异步回调
System.out.println(future2.join());

使用场景:适用于对异步任务的结果进行进一步处理,并且需要返回新的结果。 对比thenApply 是同步回调,与父任务共享线程;thenApplyAsync 是异步回调,可能会启动新的线程。

2.2 thenAccept 和 thenAcceptAsync

// 使用默认线程池
CompletableFuture<Void> future1 = CompletableFuture.supplyAsync(() -> "Hello")
    .thenAccept(s -> System.out.println(s + " World")); // 同步回调
future1.join();

// 使用自定义线程池
CompletableFuture<Void> future2 = CompletableFuture.supplyAsync(() -> "Hello", customExecutor)
    .thenAcceptAsync(s -> System.out.println(s + " World"), customExecutor); // 异步回调
future2.join();

使用场景:适用于对异步任务的结果进行处理,但不需要返回新的结果。 对比thenAccept 是同步回调,与父任务共享线程;thenAcceptAsync 是异步回调,可能会启动新的线程。

2.3 thenRun 和 thenRunAsync

// 使用默认线程池
CompletableFuture<Void> future1 = CompletableFuture.supplyAsync(() -> "Hello")
    .thenRun(() -> System.out.println("Task completed")); // 同步回调
future1.join();

// 使用自定义线程池
CompletableFuture<Void> future2 = CompletableFuture.supplyAsync(() -> "Hello", customExecutor)
    .thenRunAsync(() -> System.out.println("Task completed"), customExecutor); // 异步回调
future2.join();

使用场景:适用于在异步任务完成后执行某些操作,但不需要访问任务的结果。 对比thenRun 是同步回调,与父任务共享线程;thenRunAsync 是异步回调,可能会启动新的线程。

3. 异常处理

3.1 whenComplete 和 whenCompleteAsync

// 使用默认线程池
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    if (new Random().nextInt(2) == 0) {
        throw new RuntimeException("Error occurred");
    }
    return "Hello";
}).whenComplete((result, exception) -> {
    if (exception != null) {
        System.out.println("Exception: " + exception.getMessage());
    } else {
        System.out.println("Result: " + result);
    }
});
future1.join();

// 使用自定义线程池
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    if (new Random().nextInt(2) == 0) {
        throw new RuntimeException("Error occurred");
    }
    return "Hello";
}, customExecutor).whenCompleteAsync((result, exception) -> {
    if (exception != null) {
        System.out.println("Exception: " + exception.getMessage());
    } else {
        System.out.println("Result: " + result);
    }
}, customExecutor);
future2.join();

使用场景:适用于需要在任务完成时(无论是正常完成还是异常完成)执行某些操作。 对比whenComplete 是同步回调,与父任务共享线程;whenCompleteAsync 是异步回调,可能会启动新的线程。

3.2 handle 和 handleAsync

// 使用默认线程池
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    if (new Random().nextInt(2) == 0) {
        throw new RuntimeException("Error occurred");
    }
    return "Hello";
}).handle((result, exception) -> {
    if (exception != null) {
        return "Default Value";
    }
    return result + " World";
});
System.out.println(future1.join());

// 使用自定义线程池
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    if (new Random().nextInt(2) == 0) {
        throw new RuntimeException("Error occurred");
    }
    return "Hello";
}, customExecutor).handleAsync((result, exception) -> {
    if (exception != null) {
        return "Default Value";
    }
    return result + " World";
}, customExecutor);
System.out.println(future2.join());

使用场景:适用于需要对任务的结果或异常进行处理,并返回一个新的结果。 对比handle 是同步回调,与父任务共享线程;handleAsync 是异步回调,可能会启动新的线程。

4. 多任务组合

4.1 thenCombine 和 thenCombineAsync

// 使用默认线程池
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello")
    .thenCombine(CompletableFuture.supplyAsync(() -> " World"), (s1, s2) -> s1 + s2);
System.out.println(future1.join());

// 使用自定义线程池
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Hello", customExecutor)
    .thenCombineAsync(CompletableFuture.supplyAsync(() -> " World", customExecutor), (s1, s2) -> s1 + s2, customExecutor);
System.out.println(future2.join());

使用场景:适用于需要将两个异步任务的结果组合成一个新的结果。 对比thenCombine 是同步回调,与父任务共享线程;thenCombineAsync 是异步回调,可能会启动新的线程。

4.2 thenAcceptBoth 和 thenAcceptBothAsync

// 使用默认线程池
CompletableFuture<Void> future1 = CompletableFuture.supplyAsync(() -> "Hello")
    .thenAcceptBoth(CompletableFuture.supplyAsync(() -> " World"), (s1, s2) -> System.out.println(s1 + s2));
future1.join();

// 使用自定义线程池
CompletableFuture<Void> future2 = CompletableFuture.supplyAsync(() -> "Hello", customExecutor)
    .thenAcceptBothAsync(CompletableFuture.supplyAsync(() -> " World", customExecutor), (s1, s2) -> System.out.println(s1 + s2), customExecutor);
future2.join();

使用场景:适用于需要对两个异步任务的结果进行处理,但不需要返回新的结果。 对比thenAcceptBoth 是同步回调,与父任务共享线程;thenAcceptBothAsync 是异步回调,可能会启动新的线程。

4.3 runAfterBoth 和 runAfterBothAsync

// 使用默认线程池
CompletableFuture<Void> future1 = CompletableFuture.supplyAsync(() -> "Hello")
    .runAfterBoth(CompletableFuture.supplyAsync(() -> " World"), () -> System.out.println("Both tasks completed"));
future1.join();

// 使用自定义线程池
CompletableFuture<Void> future2 = CompletableFuture.supplyAsync(() -> "Hello", customExecutor)
    .runAfterBothAsync(CompletableFuture.supplyAsync(() -> " World", customExecutor), () -> System.out.println("Both tasks completed"), customExecutor);
future2.join();

使用场景:适用于在两个异步任务都完成后执行某些操作,但不需要访问任务的结果。 对比runAfterBoth 是同步回调,与父任务共享线程;runAfterBothAsync 是异步回调,可能会启动新的线程。

 5.等待多个任务完成执行某个操作

CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {  
    // 第一个异步操作  
});  
  
CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {  
    // 第二个异步操作  
});  
CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2);  
allFutures.join(); // 阻塞直到所有异步操作完成  
// 或者使用 thenRun 来在所有操作完成后执行某些操作  
allFutures.thenRun(() -> System.out.println("All operations completed!"));
// 多线程分块处理
List<CompletableFuture<List<FtbCultivatePositionOrgStatisticesVO>>> futures = new ArrayList<>();
// 使用CompletableFuture.allOf等待所有任务完成
CompletableFuture<Void> allTasks = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
// 等待所有任务完成
allTasks.join();
// 收集所有任务的结果
List<FtbCultivatePositionOrgStatisticesVO> results = futures.stream()
		.map(CompletableFuture::join)
		.flatMap(Collection::stream)
		.collect(Collectors.toList());
方法描述是否异步是否需要线程池返回值类型使用场景
supplyAsync创建一个有返回值的异步任务可选CompletableFuture<U>需要异步执行并返回结果的任务,如查询数据库。
runAsync创建一个无返回值的异步任务可选CompletableFuture<Void>需要异步执行但不需要返回结果的任务,如日志记录。
thenApply对任务结果进行处理,有返回值CompletableFuture<U>需要对任务结果进行转换或处理的场景,如数据格式转换。
thenApplyAsync对任务结果进行异步处理,有返回值可选CompletableFuture<U>需要对任务结果进行复杂处理且不想阻塞当前线程的场景。
thenAccept对任务结果进行处理,无返回值CompletableFuture<Void>需要对任务结果进行处理但不需要返回值的场景,如打印日志。
thenAcceptAsync对任务结果进行异步处理,无返回值可选CompletableFuture<Void>需要对任务结果进行处理但不需要返回值,且不想阻塞当前线程的场景。
thenRun任务完成后执行的操作,无入参,无返回值CompletableFuture<Void>任务完成后需要执行某些操作但不需要访问任务结果的场景。
thenRunAsync任务完成后异步执行的操作,无入参,无返回值可选CompletableFuture<Void>任务完成后需要执行某些操作但不需要访问任务结果,且不想阻塞当前线程的场景。
whenComplete任务完成后执行的操作,可以访问任务结果或异常CompletableFuture<T>需要在任务完成后执行某些操作,无论任务是否成功。
whenCompleteAsync任务完成后异步执行的操作,可以访问任务结果或异常可选CompletableFuture<T>需要在任务完成后执行某些操作,且不想阻塞当前线程。
handle任务完成后执行的操作,可以访问任务结果或异常,有返回值CompletableFuture<U>需要在任务完成后根据结果或异常返回新的值。
handleAsync任务完成后异步执行的操作,可以访问任务结果或异常,有返回值可选CompletableFuture<U>需要在任务完成后根据结果或异常返回新的值,且不想阻塞当前线程。
thenCombine组合两个任务的结果,两个任务都完成后执行的操作,有返回值CompletableFuture<U>需要组合两个任务的结果并返回新的值。
thenCombineAsync组合两个任务的结果,两个任务都完成后异步执行的操作,有返回值可选CompletableFuture<U>需要组合两个任务的结果并返回新的值,且不想阻塞当前线程。
thenAcceptBoth组合两个任务的结果,两个任务都完成后执行的操作,无返回值CompletableFuture<Void>需要组合两个任务的结果但不需要返回值。
thenAcceptBothAsync组合两个任务的结果,两个任务都完成后异步执行的操作,无返回值可选CompletableFuture<Void>需要组合两个任务的结果但不需要返回值,且不想阻塞当前线程。
runAfterBoth两个任务都完成后执行的操作,无入参,无返回值CompletableFuture<Void>两个任务完成后需要执行某些操作但不需要访问任务结果。
runAfterBothAsync两个任务都完成后异步执行的操作,无入参,无返回值可选CompletableFuture<Void>两个任务完成后需要执行某些操作但不需要访问任务结果,且不想阻塞当前线程。
applyToEither两个任务中任意一个完成后执行的操作,有返回值CompletableFuture<U>两个任务中任意一个完成后需要根据结果返回新的值。
applyToEitherAsync两个任务中任意一个完成后异步执行的操作,有返回值可选CompletableFuture<U>两个任务中任意一个完成后需要根据结果返回新的值,且不想阻塞当前线程。
acceptEither两个任务中任意一个完成后执行的操作,无返回值CompletableFuture<Void>两个任务中任意一个完成后需要执行某些操作但不需要返回值。
acceptEitherAsync两个任务中任意一个完成后异步执行的操作,无返回值可选CompletableFuture<Void>两个任务中任意一个完成后需要执行某些操作但不需要返回值,且不想阻塞当前线程。
runAfterEither两个任务中任意一个完成后执行的操作,无入参,无返回值CompletableFuture<Void>两个任务中任意一个完成后需要执行某些操作但不需要访问任务结果。
runAfterEitherAsync两个任务中任意一个完成后异步执行的操作,无入参,无返回值可选CompletableFuture<Void>两个任务中任意一个完成后需要执行某些操作但不需要访问任务结果,且不想阻塞当前线程。
allOf等待多个任务都完成,无返回值CompletableFuture<Void>需要等待多个任务都完成后才继续执行。
anyOf等待多个任务中任意一个完成,有返回值CompletableFuture<Object>多个任务中任意一个完成后需要根据结果返回新的值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2335095.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

摄影测量——单像空间后方交会

空间后方交会的求解是一个非线性问题&#xff0c;通常采用最小二乘法进行迭代解算。下面我将详细介绍具体的求解步骤&#xff1a; 1. 基本公式&#xff08;共线条件方程&#xff09; 共线条件方程是后方交会的基础&#xff1a; 复制 x - x₀ -f * [m₁₁(X-Xₛ) m₁₂(Y-…

基于RV1126开发板的人脸姿态估计算法开发

1. 人脸姿态估计简介 人脸姿态估计是通过对一张人脸图像进行分析&#xff0c;获得脸部朝向的角度信息。姿态估计是多姿态问题中较为关键的步骤。一般可以用旋转矩阵、旋转向量、四元数或欧拉角表示。人脸的姿态变化通常包括上下俯仰(pitch)、左右旋转(yaw)以及平面内角度旋转(r…

鲲鹏+昇腾部署集群管理软件GPUStack,两台服务器搭建双节点集群【实战详细踩坑篇】

前期说明 配置&#xff1a;2台鲲鹏32C2 2Atlas300I duo&#xff0c;之前看网上文档&#xff0c;目前GPUstack只支持910B芯片&#xff0c;想尝试一下能不能310P也部署试试&#xff0c;毕竟华为的集群软件要收费。 系统&#xff1a;openEuler22.03-LTS 驱动&#xff1a;24.1.rc…

机器学习中 提到的张量是什么?

在机器学习中, 张量(Tensor) 是一个核心数学概念,用于表示和操作多维数据。以下是关于张量的详细解析: 一、数学定义与本质 张量在数学和物理学中的定义具有多重视角: 多维数组视角 传统数学和物理学中,张量被定义为多维数组,其分量在坐标变换时遵循协变或逆变规则。例…

edge 更新到135后,Clash 打开后,正常网页也会自动跳转

发现了一个有意思的问题&#xff1a;edge 更新135后&#xff0c;以前正常使用的clash出现了打开deepseek也会自动跳转&#xff1a; Search Resultshttps://zurefy.com/zu1.php#gsc.tab0&gsc.qdeepseek &#xff0c;也就是不需要梯子的网站打不开了&#xff0c;需要的一直正…

prime 1 靶场笔记(渗透测试)

环境说明&#xff1a; 靶机prime1和kali都使用的是NAT模式&#xff0c;网段在192.168.144.0/24。 Download (Mirror): https://download.vulnhub.com/prime/Prime_Series_Level-1.rar 一.信息收集 1.主机探测&#xff1a; 使用nmap进行全面扫描扫描&#xff0c;找到目标地址及…

第16届蓝桥杯单片机模拟试题Ⅲ

试题 代码 sys.h #ifndef __SYS_H__ #define __SYS_H__#include <STC15F2K60S2.H> //sys.c extern unsigned char UI; //界面标志(0湿度界面、1参数界面、2时间界面) extern unsigned char time; //时间间隔(1s~10S) extern bit ssflag; //启动/停止标志…

打造现代数据基础架构:MinIO对象存储完全指南

目录 打造现代数据基础架构&#xff1a;MinIO对象存储完全指南1. MinIO介绍1.1 什么是对象存储&#xff1f;1.2 MinIO核心特点1.3 MinIO使用场景 2. MinIO部署方案对比2.1 单节点单驱动器(SNSD/Standalone)2.2 单节点多驱动器(SNMD/Standalone Multi-Drive)2.3 多节点多驱动器(…

OOM问题排查和解决

问题 java.lang.OutOfMemoryError: Java heap space 排查 排查手段 jmap命令 jmap -dump,formatb,file<file-path> <pid> 比如 jmap -dump:formatb,file./heap.hprof 44532 使用JVisualVM工具&#xff1a; JVisualVM是一个图形界面工具&#xff0c;它可以帮…

「出海匠」借助CloudPilot AI实现AWS降本60%,支撑AI电商高速增长

&#x1f50e;公司简介 「出海匠」&#xff08;chuhaijiang.com&#xff09;是「数绘星云」公司打造的社交内容电商服务平台&#xff0c;专注于为跨境生态参与者提供数据支持与智能化工作流。平台基于大数据与 AI 技术&#xff0c;帮助商家精准分析市场趋势、优化运营策略&…

【Python爬虫】简单案例介绍3

本文继续接着我的上一篇博客【Python爬虫】简单案例介绍2-CSDN博客 目录 3.3 代码开发 3.3 代码开发 编写代码的步骤&#xff1a; request请求科普中国网站地址url&#xff0c;解析得到类名为"list-block"的div标签。 for循环遍历这个div列表里的每个div&#xff0…

swift菜鸟教程6-10(运算符,条件,循环,字符串,字符)

一个朴实无华的目录 今日学习内容&#xff1a;1.Swift 运算符算术运算符比较运算符逻辑运算符位运算符赋值运算区间运算符其他运算符 2.Swift 条件语句3.Swift 循环4.Swift 字符串字符串属性 isEmpty字符串常量let 变量var字符串中插入值字符串连接字符串长度 String.count使用…

如何通过技术手段降低开发成本

通过技术手段降低开发成本的关键在于&#xff1a; 自动化工具的使用、优化开发流程、云计算资源的利用、开发技术栈的精简与创新、团队协作平台的高效管理。 其中&#xff0c;自动化工具的使用是最为有效的技术手段之一。自动化工具通过减少人工干预和重复性工作&#xff0c;大…

Ubuntu上docker、docker-compose的安装

今天来实践下Ubuntu上面安装docker跟docker-compose&#xff0c;为后面安装dify、fastgpt做准备。 一、安装docker sudo apt-get updatesudo apt-get install docker.io 然后系统输入 docker --version 出现下图即为docker安装成功。 二、安装docker-compose 我先看下系统…

OpenCV图像处理进阶教程:几何变换与频域分析全解析

OpenCV图像处理进阶教程&#xff1a;几何变换与频域分析全解析 &#x1f4da; 本文提供了OpenCV图像处理的核心操作详解&#xff0c;从基础的几何变换到高级的频域分析&#xff0c;代码示例清晰易懂&#xff0c;实用性强。完整代码已开源至GitHub&#xff1a;https://github.co…

AJAX与Axios基础

目录 一、AJAX 核心概念解析 1.1 AJAX 的核心概念 1.2 AJAX 工作原理 1.3 AJAX 局限性 二、axios 库介绍 2.1 Axios 核心特性 2.2 快速上手 2.3 核心配置项 2.4 错误处理标准方案 三、Axios 核心配置项 3.1 常用核心配置项 1. url 2. method 3. params 4. data …

[OS] vDSO + vvar(频繁调用的处理) | 存储:寄存器(高效)和栈(空间大)| ELF标准包装规范(加速程序加载)

vDSO vvar 一、社区公告板系统&#xff08;类比 vDSO vvar&#xff09; 想象你住在一个大型社区&#xff0c;管理员&#xff08;内核&#xff09;需要向居民&#xff08;用户程序&#xff09;提供实时信息&#xff08;如天气预报、社区活动时间等&#xff09;。直接让每个居…

Sentinel源码—1.使用演示和简介二

大纲 1.Sentinel流量治理框架简介 2.Sentinel源码编译及Demo演示 3.Dashboard功能介绍 4.流控规则使用演示 5.熔断规则使用演示 6.热点规则使用演示 7.授权规则使用演示 8.系统规则使用演示 9.集群流控使用演示 5.熔断规则使用演示 (1)案例说明熔断和降级 (2)Sentin…

IDEA的常用设置(更新中......)

文章目录 1. 自动导包2. 忽略大小写3. 设置项目文件编码格式4. 设置方法之间分割线5. 设置字体大小6. 设置IDEA默认不打开项目持续更新中...... 1. 自动导包 File->Settings->Editor->General>Auto Import 2. 忽略大小写 File->Editor->General->Code…

c# Kestrel

Kestrel 是 .NET 中用于 ASP.NET Core 应用程序的跨平台 Web 服务器。它是轻量级且高性能的&#xff0c;能够处理大量并发连接&#xff0c;常被用作 ASP.NET Core 应用的默认服务器。以下为你介绍 Kestrel 的基本使用和配置&#xff1a; 基本使用 创建一个简单的 ASP.NET Cor…