当前位置: 首页 > news >正文

掌握CompletableFuture,提升你的代码效率!

在这里插入图片描述

文章目录

    • 1 CompletableFuture与线程池之间有什么关系?
    • 2 如何优化CompletableFuture的性能?
    • 3 实际项目中,以并行执行多个HTTP请求为例,你会如何优雅使用CompletableFuture 解决问题?

1 CompletableFuture与线程池之间有什么关系?

CompletableFuture 和线程池的关系是,CompletableFuture 用来定义和管理异步任务,而线程池则负责实际执行这些任务。

想象一下,CompletableFuture 是一个能够在未来某个时刻完成任务的代表。它可以让代码更容易处理异步操作,也就是任务在后台运行,不会阻塞主线程。

在进行耗时的网络请求或者复杂的计算时,就可以用 CompletableFuture 来处理这些操作,并在任务完成时取得结果。

线程池则是负责实际执行这些任务的地方。它可以被看作是一组可以执行任务的工作线程。这些线程在后台待命,等待被分配任务。当任务到达线程池时,线程池会从这些线程中选择一个,分配任务,然后开始执行。

创建一个 CompletableFuture 时,通常需要指定一个线程池来处理实际的任务。

// 创建了一个包含四个线程的线程池
ExecutorService executor = Executors.newFixedThreadPool(4);// 使用 supplyAsync 方法创建一个 CompletableFuture 时,可以传递一个 Executor,这个 Executor 就是线程池的实现
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {// 模拟耗时的任务try {Thread.sleep(2000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}return "Task Completed";
}, executor);

线程池的作用在于管理这些线程的生命周期和调度,避免每次执行任务时都创建新的线程,这样可以提高效率,减少资源消耗。线程池中的线程会在任务完成后继续等待新的任务,从而避免了频繁的线程创建和销毁开销。

当任务完成时,CompletableFuture 会获取任务的结果。使用线程池执行的任务可以利用 CompletableFuture 提供的各种功能,比如处理异常、组合多个异步任务等:

// 在任务完成时打印结果,如果有异常发生,则会打印错误信息
future.thenAccept(result -> {System.out.println("Result: " + result);
}).exceptionally(ex -> {System.err.println("Error: " + ex.getMessage());return null;
});

2 如何优化CompletableFuture的性能?

优化 CompletableFuture 的性能需要关注几个关键方面。

一个重要的策略是合理使用线程池。可以创建一个固定大小的线程池来避免过多的线程切换开销,通过 Executors.newFixedThreadPool(int nThreads) 创建线程池,nThreads 的值应与系统核心数匹配,确保资源得到有效利用。

团队工作效率不仅仅取决于每个人的能力,还与分配的资源有关。如果团队人数太多或太少,都会影响整体效率。

默认情况下,supplyAsync 使用的是公共的 ForkJoinPool,但在高负载时,这可能成为瓶颈,可以考虑创建并配置自定义的线程池来适应具体的任务负载:

public static void main(String[] args) {ExecutorService customThreadPool = Executors.newFixedThreadPool(10);CompletableFuture.supplyAsync(() -> fetchData("http://api.com"), customThreadPool).thenAccept(result -> System.out.println(result));// 关闭线程池customThreadPool.shutdown();}private static String fetchData(String url) {// 模拟 HTTP 请求return "Response from " + url;}

处理多个异步任务时,合并操作能够减少资源浪费。使用 CompletableFuture.allOf(futures) 可以等待所有给定的 CompletableFuture 完成,这样可以减少不必要的等待时间。如果只需要等待其中一个任务完成,CompletableFuture.anyOf(futures) 是更合适的选择,它会在第一个完成的任务完成后立即返回结果。

在一个团队中,如果每个人都独立完成自己的任务,最终合并结果的过程可能会很麻烦。

如果需要同时处理多个异步任务,可以使用 allOf 合并它们。比如,如果有多个 API 请求可以并行处理,可以像这样将它们合并:

public static void main(String[] args) {CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> fetchData("http://api1.com"));CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> fetchData("http://api2.com"));CompletableFuture<Void> allOf = CompletableFuture.allOf(future1, future2);allOf.thenRun(() -> {try {System.out.println("Result from API 1: " + future1.get());System.out.println("Result from API 2: " + future2.get());} catch (Exception e) {e.printStackTrace();}});}private static String fetchData(String url) {// 模拟 HTTP 请求return "Response from " + url;}

避免阻塞操作也至关重要。在异步操作中,阻塞线程会影响整体性能。尽量将耗时操作放在异步任务内部,而不是在主线程中进行等待,这样可以减少线程占用,提高执行效率。

想象一个在办公室里的团队成员,他们需要处理多项任务。如果有人在处理一个任务时不停地打电话查询进度,这就像是阻塞操作,导致其他任务也被拖慢。

当需要链式处理多个异步结果时,thenCompose 方法可以提供更高的性能和清晰度。thenCompose 用于处理返回的 CompletableFuture,可以避免多层嵌套,使得代码更简洁。

CompletableFuture.supplyAsync(() -> {// 第一个异步任务return "Hello";
}).thenCompose(result -> {// 处理第一个任务的结果,并返回另一个 CompletableFuturereturn CompletableFuture.supplyAsync(() -> result + " World");
}).thenAccept(System.out::println);

处理异常时,使用 exceptionallyhandle 方法可以提升代码的健壮性。exceptionally 可以处理任务执行中的异常,handle 既处理正常结果也处理异常。这样能确保任务在遇到问题时仍能正常运行或提供有效的错误信息。

任务拆分也是一种优化策略。将大任务拆分成多个小任务可以提高并发性和效率。这样做能更好地利用线程池,并且可以更精确地控制任务的执行。

监控和调优是优化性能的基础。使用工具来监控线程池的使用情况和 CompletableFuture 的执行状态,可以及时发现并解决瓶颈问题,通过实际测试和分析,调整线程池大小和任务拆分策略,从而实现最佳性能。

3 实际项目中,以并行执行多个HTTP请求为例,你会如何优雅使用CompletableFuture 解决问题?

在实际项目中,使用 CompletableFuture 来并行执行多个 HTTP 请求可以显著提高效率。比如,考虑一个场景,需要从多个 API 获取数据。

分析:创建 CompletableFuture 实例并执行异步 HTTP 请求时,可以使用 CompletableFuture.supplyAsync 方法。这个方法接受一个 Supplier,在这个 Supplier 中执行实际的 HTTP 请求。每个请求都可以用一个 CompletableFuture 处理,从而实现并行执行。

如何处理多个 HTTP 请求

import java.util.concurrent.CompletableFuture;
import java.util.List;public class AsyncHttpRequests {public static void main(String[] args) {// 包含了多个要请求的 URLList<String> urls = List.of("http://api1.com", "http://api2.com", "http://api3.com");// 创建了一组 CompletableFuture 实例,每一个都负责异步执行一个 HTTP 请求// fetchData(url) 方法模拟了实际的 HTTP 请求逻辑,这里可以用真实的 HTTP 客户端代替List<CompletableFuture<String>> futures = urls.stream().map(url -> CompletableFuture.supplyAsync(() -> fetchData(url))).toList();// 等待所有异步请求完成,接收一个 CompletableFuture 数组,并在所有请求完成后继续执行后续操作。CompletableFuture<Void> allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));// thenApply 方法将所有请求结果汇集起来,提取每个 CompletableFuture 的结果并汇集成一个列表CompletableFuture<List<String>> allOfResult = allOf.thenApply(v -> futures.stream().map(CompletableFuture::join).toList());// 将所有结果打印到控制台allOfResult.thenAccept(results -> {results.forEach(System.out::println);});}private static String fetchData(String url) {// 模拟 HTTP 请求,这里应使用实际的 HTTP 请求逻辑return "Response from " + url;}
}

使用 CompletableFuture 可以优雅地处理多个异步任务,通过合并结果和处理异常,可以确保程序在高负载时表现稳定。

必须从过去的错误学习教训而非依赖过去的成功


http://www.mrgr.cn/news/14198.html

相关文章:

  • CSS中的align-content属性:实现垂直居中的新方式
  • 综合能源充电站有序充电策略
  • Maven 打包如何跳过测试
  • 深度强化学习算法(四)(附带MATLAB程序)
  • Linux 安装Mysql保姆级教程
  • 2024年【制冷与空调设备运行操作】考试及制冷与空调设备运行操作考试资料
  • 【实战指南】RESTful 从入门到精通(Spring Boot)
  • ChatGPT 3.5/4.0新手使用手册
  • 从事大模型研发的技术栈和学习路线
  • 微信小程序获取用户openId并通过服务端向用户发送模板消息
  • gin快速入门
  • AI:引领未来的科技浪潮
  • 解决Springboot项目Maven下载依赖速度慢的问题
  • 企业财税自动化解决方案的优势与应用
  • 仿人机器人
  • 制造业如何利用MES管理系统实现数据采集
  • 2024 Python3.10 系统入门+进阶(十一):Python解析式和生成器表达式
  • 【爬虫软件】采集抖音博主的主页发布作品
  • QPS提升10倍怎么设计-JAVA后端经常用到的技术
  • 什么是杨氏模量