CompletableFuture 异步与最佳实践

news2024/11/18 11:43:44

文章目录

    • CompletableFuture 异步-前言
    • 背景
    • 主要特性
      • 1. 异步任务执行
      • 2. 任务组合
      • 3. 异常处理
      • 4. 回调机制
    • 最佳实践
      • 并行调用多个接口
      • 处理异常
      • 超时设置
      • 使用 Executor 自定义线程池
      • 总结
    • 总结

CompletableFuture 异步-前言

在现代Java开发中,处理并发任务和异步编程已成为关键需求。Java 8引入的 CompletableFuture 提供了一种强大的方式来简化异步编程,使其更加直观和易于维护。与传统的线程或 Future 方法相比,CompletableFuture 提供了更高层次的抽象,允许开发者以声明式的方式编写异步代码。这篇文章将探讨 CompletableFuture 的基本概念和用法,帮助你掌握这一强大的工具,提升Java应用程序的性能和响应性。本文章提供CompletableFuture的基础用法和最佳实践。

背景

在早期的Java版本中,处理并发任务主要依赖于 Thread 类和 Executor 框架。这些方法虽然强大,但在处理复杂的异步流程时显得笨重和难以管理。Future 接口引入后,提供了一个表示异步计算结果的方式,但它的局限性在于获取结果时必须调用阻塞方法 get(),无法方便地进行非阻塞操作和链式调用。

为了解决这些问题,Java 8引入了 CompletableFuture,它不仅实现了 Future 接口,还提供了丰富的API,用于创建、组合和处理异步任务。通过使用 CompletableFuture,开发者可以编写更清晰、简洁的异步代码,避免复杂的回调地狱问题。

主要特性

CompletableFuture 的主要特性包括:

  1. 异步任务执行:可以轻松地在后台线程中执行任务,而不阻塞主线程。
  2. 任务组合:支持多种方式将多个异步任务组合在一起,如 thenCompose 和 thenCombine。
  3. 异常处理:提供了处理异步任务中异常的便捷方法,如 handle 和 exceptionally。
  4. 回调机制:允许在任务完成时自动执行回调函数,而不需要显式地阻塞等待结果。
    在这里插入图片描述

1. 异步任务执行

CompletableFuture 可以轻松地在后台线程中执行任务,而不阻塞主线程。例如:

   @Test
    public void testAsyncTaskExecution() throws ExecutionException, InterruptedException, TimeoutException {
        System.out.println("启动异步线程执行任务");
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("正在执行异步线程任务");
            // 模拟异步任务
            return "Hello, Async!";
        });
        System.out.println("执行主线程任务");
        // 验证结果
        System.out.println("等待异步线程执行完成");
        String result = future.get(1, TimeUnit.SECONDS);
        System.out.println("异步线程执行完毕,打印结果");
        System.out.println(result);
    }

测试结果如下:

启动异步线程执行任务
执行主线程任务
等待异步线程执行完成
正在执行异步线程任务
异步线程执行完毕,打印结果
Hello, Async!

2. 任务组合

用于将一个异步任务的结果作为下一个异步任务的输入。

    @Test
    public void testTaskComposition() throws ExecutionException, InterruptedException, TimeoutException {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "任务1执行结果")
                .thenCompose(result -> CompletableFuture.supplyAsync(() -> result + " 任务2执行结果"));

        // 验证结果
        String result = future.get(1, TimeUnit.SECONDS);
        System.out.println(result);
    }

结果如下:

任务1执行结果 任务2执行结果

3. 异常处理

CompletableFuture 提供了处理异步任务中异常的便捷方法:

    @Test
    public void testExceptionHandling() throws ExecutionException, InterruptedException, TimeoutException {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            if (true) {
                throw new RuntimeException("任务执行过程中出现异常。");
            }
            return "任务执行成功";
        }).exceptionally(ex -> "任务执行出现异常,返回一个默认值");

        // 验证结果
        String result = future.get(1, TimeUnit.SECONDS);
        System.out.println(result);
    }
任务执行出现异常,返回一个默认值

4. 回调机制

CompletableFuture 允许在任务完成时自动执行回调函数,而不需要显式地阻塞等待结果:

    @Test
    public void testCallbackMechanism() throws ExecutionException, InterruptedException, TimeoutException {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "任务执行成功。")
                .thenApply(result -> result + "回调方法执行成功。");

        // 验证结果
        String result = future.get(1, TimeUnit.SECONDS);
        System.out.println(result);
    }
任务执行成功。回调方法执行成功。

最佳实践

处理多个数据统计接口是一项常见的需求,尤其是在微服务架构或分布式系统中。CompletableFuture 提供了强大的工具来并行调用这些接口,汇总结果,并提高整体性能。以下是一些最佳实践,帮助你高效地处理多个数据统计接口。

并行调用多个接口

使用 CompletableFuture 的异步特性,可以并行调用多个接口,避免顺序调用带来的性能瓶颈。

示例如下:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.List;

public class StatisticsService {

    public CompletableFuture<Integer> fetchStatisticA() {
        return CompletableFuture.supplyAsync(() -> {
            // 模拟调用接口 A
            try {
                TimeUnit.SECONDS.sleep(1); // 模拟延迟
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 100;
        });
    }

    public CompletableFuture<Integer> fetchStatisticB() {
        return CompletableFuture.supplyAsync(() -> {
            // 模拟调用接口 B
            try {
                TimeUnit.SECONDS.sleep(2); // 模拟延迟
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 200;
        });
    }

    public CompletableFuture<Integer> fetchStatisticC() {
        return CompletableFuture.supplyAsync(() -> {
            // 模拟调用接口 C
            try {
                TimeUnit.SECONDS.sleep(3); // 模拟延迟
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 300;
        });
    }

    public int aggregateStatistics() throws ExecutionException, InterruptedException, TimeoutException {
        CompletableFuture<Integer> futureA = fetchStatisticA();
        CompletableFuture<Integer> futureB = fetchStatisticB();
        CompletableFuture<Integer> futureC = fetchStatisticC();

        CompletableFuture<Void> allOf = CompletableFuture.allOf(futureA, futureB, futureC);

        // 等待所有任务完成
        allOf.get(5, TimeUnit.SECONDS);

        // 聚合结果
        int resultA = futureA.get();
        int resultB = futureB.get();
        int resultC = futureC.get();

        return resultA + resultB + resultC;
    }

    public static void main(String[] args) {
        StatisticsService service = new StatisticsService();
        try {
            int total = service.aggregateStatistics();
            System.out.println("Total Statistics: " + total);
        } catch (ExecutionException | InterruptedException | TimeoutException e) {
            e.printStackTrace();
        }
    }
}

解释

  • 异步任务fetchStatisticAfetchStatisticBfetchStatisticC 分别异步调用三个不同的数据统计接口。
  • 并行执行:使用 CompletableFuture.supplyAsync 方法并行调用这些接口。
  • 聚合结果:使用 CompletableFuture.allOf 等待所有异步任务完成后,再聚合结果。

测试结果如下:

Total Statistics: 600

处理异常

在调用多个接口时,某些接口可能会失败。使用 CompletableFuture 提供的异常处理方法,可以优雅地处理这些情况。
示例如下:

public int aggregateStatisticsWithExceptionHandling() {
    CompletableFuture<Integer> futureA = fetchStatisticA()
        .exceptionally(ex -> {
            System.err.println("Failed to fetch statistic A: " + ex.getMessage());
            return 0;
        });

    CompletableFuture<Integer> futureB = fetchStatisticB()
        .exceptionally(ex -> {
            System.err.println("Failed to fetch statistic B: " + ex.getMessage());
            return 0;
        });

    CompletableFuture<Integer> futureC = fetchStatisticC()
        .exceptionally(ex -> {
            System.err.println("Failed to fetch statistic C: " + ex.getMessage());
            return 0;
        });

    CompletableFuture<Void> allOf = CompletableFuture.allOf(futureA, futureB, futureC);

    try {
        // 等待所有任务完成
        allOf.get(5, TimeUnit.SECONDS);

        // 聚合结果
        int resultA = futureA.get();
        int resultB = futureB.get();
        int resultC = futureC.get();

        return resultA + resultB + resultC;
    } catch (ExecutionException | InterruptedException | TimeoutException e) {
        e.printStackTrace();
        return 0;
    }
}

解释:

  • 异常处理:使用 exceptionally 方法为每个异步任务添加异常处理逻辑。当任务失败时,提供默认值(如0)。
  • 聚合结果:同样地,在所有任务完成后,聚合结果,并返回总统计值。

超时设置

为了防止单个接口调用过慢导致整体延迟,可以为每个异步任务设置超时时间。

public CompletableFuture<Integer> fetchStatisticAWithTimeout() {
    return CompletableFuture.supplyAsync(() -> {
        // 模拟调用接口 A
        try {
            TimeUnit.SECONDS.sleep(1); // 模拟延迟
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 100;
    }).completeOnTimeout(0, 2, TimeUnit.SECONDS); // 超时设置
}

解释

  • 超时设置:使用 completeOnTimeout 方法为异步任务设置超时,当超时时返回默认值(如0)。

使用 Executor 自定义线程池

默认情况下,CompletableFuture 使用 ForkJoinPool.commonPool() 作为线程池。可以通过自定义线程池来控制异步任务的执行资源。

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;

public class StatisticsService {

    private final ExecutorService executor = Executors.newFixedThreadPool(10);

    public CompletableFuture<Integer> fetchStatisticA() {
        return CompletableFuture.supplyAsync(() -> {
            // 模拟调用接口 A
            try {
                TimeUnit.SECONDS.sleep(1); // 模拟延迟
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 100;
        }, executor);
    }

    // 同样地,fetchStatisticB 和 fetchStatisticC 方法也可以使用 executor 进行调用

    public void shutdown() {
        executor.shutdown();
    }
}

解释:

  • 自定义线程池:使用 Executors.newFixedThreadPool 创建一个固定大小的线程池,传递给 supplyAsync
    方法以控制异步任务的执行。

总结

通过 CompletableFuture,你可以高效地并行调用多个数据统计接口,并在这些任务完成后聚合结果。上述最佳实践包括并行调用、异常处理、超时设置以及自定义线程池,帮助你编写更健壮和高效的异步代码。通过这些实践,你可以提升应用程序的性能和用户体验。

总结

CompletableFuture 是 Java 异步编程的利器,通过它可以简化异步任务的创建、组合和处理。掌握 CompletableFuture 可以帮助你编写更高效、更易维护的异步代码,提升应用程序的性能和用户体验。

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

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

相关文章

Nginx配置及优化

Nginx配置及优化 前言nginx.conf拆分理解上线 最近在配置Nginx的时候&#xff0c;偶尔一些细致的理论有些模糊&#xff0c;配置起来费了点功夫&#xff0c;今天来详细写一下我个人的理解&#xff0c;文章参考了一些官网和其他优秀博主的文章http://t.csdnimg.cn/GbID9。 前言 …

[leetcode hot150]第二百三十六题,二叉树的最近公共祖先

题目&#xff1a; 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个…

测试基础07:测试工作流程规范、进度同步与把控

课程大纲 1、迭代测试流程 2、测试流程 2.1、测试用例评审 目的&#xff1a;对齐产品需求理解&#xff0c;完善、优化测试场景。 参与方&#xff1a;项目、产品、开发、测试。 用例内容&#xff1a;冒烟用例&#xff08;主流程&#xff09; 功能用例。 2.2、冒烟测试 提测…

ADF: 获取Data Lake Storage上的文件列表并根据文件名删除文件

假设 Data Lake 上有个test的文件夹&#xff0c;有如下文件 目标&#xff1a;使用Azure Data Factory的Pipeline获取这个目录下的文件名列表&#xff0c;并删除掉以"ETC"开头的文件。 步骤&#xff1a; 1. 需要在Linked services中新建一个能连接到Data Lake的连接…

javaScript 换行符的使用

最近新接收了一个项目&#xff0c;再做一个小功能点时&#xff0c;字符串需要增加换行&#xff0c;小小记录一下 1、标签换行 <br> 2、字符换行 \n&#xff0c;换行就是转到下一行输出。例如&#xff1a; console.log("hello\nworld!"); 在终端中将输出 he…

橙派探险记:开箱香橙派 AIpro 与疲劳驾驶检测的奇幻之旅

目录 引子&#xff1a;神秘包裹的到来 第一章&#xff1a;香橙派AIpro初体验 资源与性能介绍 系统烧录 Linux 镜像&#xff08;TF 卡&#xff09; 调试模式 登录模式 第二章&#xff1a;大胆的项目构想 系统架构设计 香橙派 AIpro 在项目中的重要作用 第三章&#xf…

C++模拟实现stack和queue

1 stack 1.1概念 stl栈 1.2栈概念 1.3代码 2 queue 2.1概念 stl队列 2.2队列概念 2.3代码

OLED显示一张图片

1.思路: void Oled_Show_Image(unsigned char *image) // { unsigned char i; //-128 ~ 127位 unsigned int j; //j要重新定义&#xff0c;因为要到达图片的最后一位 //行 i没有问题&#xff0c;j有问题 i为1时&am…

Ps系统教程03

选区工具的组合使用 先用魔棒将大致区域点击圈主 会发现一些零散的小区域 使用套索工具进行区域的加减&#xff08;按住shift/alt键进行相关区域加减&#xff09; 可以放大查看 基本处理完细节之后 如果把不用的填充背景直接按delete删除&#xff0c;那么原版图案就会…

QT C++ 读写mySQL数据库 图片 例子

在上篇文章中描述了怎样搭建读写数据库的环境。 本文更进一步&#xff0c;描述了读写mySQL数据库&#xff0c;字符、整型数字、图片。读写图片相对难点。 数据库的图片字段用BLOB&#xff0c;如果图片较大要用longblob,否则会报错。 另外&#xff0c;读写数据库都使用了短连…

DNS分离解析、多域名解析以及DNS子域试验

一、DNS分离解析 有些时候&#xff0c;对不同网络中的计算机进行DNS解析时&#xff0c;需要进行"区别对待"&#xff0c;不同的主机请求服务器解析相同的域名&#xff0c;得到的IP地址也会不同。比如来自内网和外网的不同网段地址的客户机请求解析同一域名时&#xff…

简介有限面积和无限周长

前言 分形理论是一种非常重要的科学概念,它被广泛应用于物理学、数学、生物学等领域。分形理论描述了一种重复自相似的结构,这种结构在不同的尺度上都具有类似的形态。由于分形理论的应用广泛且深远,了解分形理论可以帮助人们更好地理解自然界和人造世界中的现象。 作为一…

docker占用磁盘空间大小排查

首先进入到 /var/lib/docker/overlay2 目录下,查看谁占用的较多 cd /var/lib/docker/overlay2/du -s ./* | sort -rn | more再通过目录名查找容器名 docker ps -q | xargs docker inspect --format {{.State.Pid}}, {{.Id}}, {{.Name}}, {{.GraphDriver.Data.WorkDir}} | gre…

基于Patroni+etcd+流复制搭建PostgreSQL高可用——筑梦之路

Patroni方案简介 Patroni是一个基于zk、etcd、consul等的pg ha模板&#xff0c;可以使用python来创建和定制高可用性解决方案。Patroni使用分布式key-value数据库作为数据存储&#xff0c;主节点故障时进行主节点重新选举。通过PG内置的流复制&#xff0c;支持同步和异步复制。…

java中,怎样用最简单方法实现写word文档

在跨平台环境中实现写word时&#xff0c;如果用现成的库&#xff0c;就会涉及跨平台兼容性问题&#xff0c;比如在安卓与java中实现写word的功能。还有一个问题就是&#xff0c;完全用程序生成word文档&#xff0c;工作量较大。所以采用了模板替换的方法。 docx文档本质就是一…

BUUCTF [GUET-CTF2019]zips 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 得到的 flag 请包上 flag{} 提交。 密文&#xff1a; 得到一个attachment.zip文件 解题思路&#xff1a; 1、解压attachment.zip&#xff0c;得到222.zip文件。尝试解压需要密码&#xff0c;使用Ziperello爆破密码…

C语言学习笔记-- 3.4.2实型变量

1.实型数据在内存中的存放形式&#xff08;了解&#xff09; 实型数据一般占4个字节&#xff08;32位&#xff09;内存空间。按指数形式存储。 2.实型变量的分类&#xff08;掌握&#xff09; 实型变量分为&#xff1a;单精度&#xff08;float型&#xff09;、双精度&#…

在线思维导图编辑!3个AI思维导图生成软件推荐!

思维导图&#xff0c;一种以创新为驱动的视觉化思考工具&#xff0c;已经渗透到我们日常生活和工作的各个角落。当我们需要整理思绪、规划项目或者梳理信息时&#xff0c;思维导图总能提供极大的帮助。 近些年随着云服务等基础设施的完善&#xff0c;我们可以看到越来越多提供…

new CCDIKSolver( OOI.kira, iks ); // 创建逆运动学求解器

demo案例 new CCDIKSolver(OOI.kira, iks); 在使用某个特定的库或框架来创建一个逆运动学&#xff08;Inverse Kinematics, IK&#xff09;求解器实例。逆运动学在机器人学、动画和计算机图形学等领域中非常重要&#xff0c;它用于根据期望的末端执行器&#xff08;如机器人的…

【IOT】OrangePi+HomeAssistant+Yolov5智能家居融合

前言 本文将以OrangePi AIpro为基础&#xff0c;在此基础构建HomeAssistant、YOLO目标检测实现智能家居更加灵活智能的场景实现。 表头表头设备OrangePi AIpro(8T)系统版本Ubuntu 22.04.4 LTSCPU4核64位处理器 AI处理器AI算力AI算力 8TOPS算力接口HDMI2、GPIO接口、Type-C、M.2…