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

news2024/9/20 20:47:32

在这里插入图片描述

文章目录

    • 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 -> {
    // 处理第一个任务的结果,并返回另一个 CompletableFuture
    return 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) {
        // 包含了多个要请求的 URL
        List<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.coloradmin.cn/o/2085617.html

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

相关文章

计算机毕业设计选题推荐-在线音乐网站-音乐专辑商城-Java/Python项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

埃隆·马斯克超级计算新里程碑:Cortex AI超级集群震撼亮相!

本周&#xff0c;科技界的超级明星埃隆马斯克再次引领潮流&#xff0c;他在超级计算领域的征途上迈出了令人瞩目的步伐。通过一段视频&#xff0c;他首次公开了最新命名的“Cortex”人工智能超级集群&#xff0c;这一壮举不仅标志着特斯拉“Giga Texas”工厂的又一次重大扩张&a…

LeetCode_sql_day17(1843.可疑银行账户)

描述&#xff1a; 表&#xff1a;Accounts ---------------------- | Column Name | Type | ---------------------- | account_id | int | | max_income | int | ---------------------- account_id 是这张表具有唯一值的列。 每行包含一个银行账户每月最大收入的…

提供开发资料 Hi3516CV610-00B/10B/20B/00S/20S/00G/20G 七个型号配置差异

根据功能不同&#xff0c; Hi3516CV610 分为七个不同型号版本: HI3516CV610-00B HI3516CV610-00B HI3516CV610-10B HI3516CV610-20B HI3516CV610-00S HI3516CV610-20S HI3516CV610-00G HI3516CV610-20G

【书生2.1】书生大模型全链路开源体系

0 引言 书生浦语官网 开源一周年总结及回顾 1 回顾 1.1 社区生态 2 总结 书生浦语大模型的开源开放体系&#xff0c;包括技术发展、性能提升、模型架构、开源生态等。 要点: &#x1f31f; 开源开放体系涵盖数据收集、标注、训练、微调、评测、部署等全链路。 &#x1f68…

【案例64】无法从套接字读取更多的数据

问题现象 系统突然间登录报如下错误&#xff1a;SELECT * FROM sm_user WHERE user_code_q? 无法从套接字读取更多的数据 问题分析 查看nc-log.log发现大量相关报错 $$callid1723104097968-1063 $$thread[http-bio-xxx-xxx-exec-xxx] $$hostxxx$$userid#UAP# $$tsxxx-08-08…

C++竞赛初阶L1-14-第六单元-数组(31~33课)542: T456472 数组逆序重存放

题目内容 将一个数组中的值按逆序重新存放。例如&#xff0c;原来的顺序为 8,6,5,4,1。要求改为 1,4,5,6,8。 输入格式 输入为两行&#xff1a;第一行数组中元素的个数 n&#xff08;1<n≤100)&#xff0c;第二行是 n 个整数&#xff0c;每两个整数之间用空格分隔。 输出…

Windows安装PostgreSQL数据库,保姆级教程

PostgreSQL 是客户端/服务器关系数据库管理系统 (RDMS)。PostgreSQL是一个功能非常强大的、源代码开放的客户/服务器关系型数据库管理系统&#xff08;RDBMS&#xff09;。PostgreSQL 也有自己的查询语言&#xff0c;称为 pgsql。 此外&#xff0c;PostgreSQL 还支持过程语言&a…

Cesium模型封装-Point

一、初始化地图 <template><div class"cesium_map"><div id"cesiumContainer"></div></div> </template><script setup> import { reactive, ref, onMounted } from "vue"; import { Point } from &…

基于yolov8的安全帽反光衣护目镜检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv8的安全帽、反光衣及护目镜检测系统是一款集成了前沿深度学习与计算机视觉技术的智能监控系统。该系统利用YOLOv8这一尖端的目标检测模型&#xff0c;结合云计算与自动化图像处理技术&#xff0c;实现对工地、化工厂、煤矿等高风险作业区域工作人员安全…

Java—方法引用

目录 初识方法引用 方法引用的分类 引用静态方法 引用成员方法 引用构造方法 其它调用方式 类名引用成员方法 引用数组的构造方法 总结 初识方法引用 方法引用就是拿现有的方法来当做函数式接口中抽象方法的方法体。 方法引用注意事项 1. 引用处必须是函数式接口&a…

初识JAVA(上)

&#x1f381;&#x1f381;创作不易&#xff0c;关注作者不迷路&#x1f380;&#x1f380; 初识JAVA 前言一、初识JAVA1.1.Java是什么1.2.Java语言的重要性1.3 Java语言发展简史1.4 Java语言特性 二、初识Java的main方法1 main方法示例 三、注释基本规则 四、数据类型1.常量2…

入门Java第一步—>IDEA的下载与安装与JDK的环境配置(day01)

1.JDK的下载与安装 jdk的安装链接分为不同操作系统如下,点击链接跳转下载页面&#xff1a; windows操作系统JDK下载链接(按住键盘ctrl键单击链接即可)&#xff1a; 链接7天有效&#xff0c;有需要的评论区找我哈 通过网盘分享的文件&#xff1a;jdk-8u271-windows-x64.exe 链…

建筑企业数字信息化转型的建议

在现代建筑企业的管理中&#xff0c;信息化转型已成为提升效率和竞争力的关键。然而&#xff0c;在选择信息化系统时&#xff0c;企业需要慎重考虑&#xff0c;以确保系统真正适合企业的现状和未来发展。 &#x1f50d; 要选合适的&#xff0c;而非“成熟”的 信息化系统的核心…

解决 启动模拟器出现 未开启Hyper-V 的问题

~~ 解决 启动模拟器出现 未开启Hyper-V 的问题 ~~ 如果在启动模拟器时出现 未开启Hyper-V 的问题 解决方案&#xff1a; 1.打开控制面板–>点击 程序和功能 2.点击左侧&#xff1a;启用或关闭Windows功能 3.找到虚拟机平台–> 打对勾√ -->确定 &#xff08;注意…

harbor私有仓库管理(twenty-nine day)

一、harbor私有仓库管理 是python的包管理工具&#xff0c;和yum对redhat的关系是一样的 yum -y install epel-release yum -y install python2-pip pip install --upgrade pip pip list pip 8x pip install --upgrade pip pip install --upgrade pip20.3 -i https://mirror…

ElasticSearch学习笔记(四)分页、高亮、RestClient查询文档

文章目录 前言7 搜索结果处理7.2 分页7.2.1 基本使用7.2.2 深度分页7.2.3 小结 7.3 高亮7.3.1 高亮原理7.3.2 实现高亮 8 RestClient查询文档8.1 match_all查询8.2 match查询与multi_match查询8.3 精确查询8.4 布尔查询8.5 排序、分页、高亮 9 项目实战9.1 酒店搜索和分页9.2 酒…

Linux 软件包管理器yum 自动化构建工具-make/makefile

Linux 工具 linux 软件包管理器 yum 把一些常用的软件提前编译好&#xff0c;做成软件包放在一个服务器上&#xff0c;通过包管理器可以很方便的获取到在这个编译好的软件包。直接进行安装。 软件包和软件包管理器就相当于 App 和应用商店这样的关系。 Linux 安装软件 源代码…

【QT】学习笔记:导出资源中静态文件

在 Qt C 中&#xff0c;可以通过将文件添加到资源文件中&#xff0c;并在程序运行时将其导出到磁盘上的指定目录。以下是具体的步骤和代码示例&#xff1a; 1. 将文件添加到资源文件中 首先&#xff0c;需要将文件添加到 Qt 的资源系统中。假设你已经创建了一个资源文件&…

力扣经典题目之->对称二叉树(镜像二叉树)

一&#xff1a;题目 本题只需在此题上稍作修改即可&#xff1a;力扣经典题目之-&#xff1e;相同的树&#xff08;递归判断两颗二叉树是否相同&#xff09;-CSDN博客 二&#xff1a;代码 解释&#xff1a; 1&#xff1a;对称二叉树本质就是左右子树的对比&#xff0c;但不是…