Java CompletableFuture 详细使用教程与实践

一、Java CompletableFuture 详细使用教程

Java 8引入了一种强大的异步编程工具:CompletableFuture。它提供了一种处理异步计算的方式,使得你可以在计算完成时获取结果,或者将一个或多个 CompletableFuture 的结果组合在一起。本部分将详细解析 CompletableFuture 的各个方面,包括创建、组合、处理异常等,并通过示例来展示其使用方法。

1.1 创建 CompletableFuture

创建 CompletableFuture 的最简单方法是使用无参数构造函数:

CompletableFuture<Void> future = new CompletableFuture<>();

这将创建一个未完成的 CompletableFuture。你可以通过 complete 方法来完成它:

future.complete(null);

如果你想创建一个已经完成的 CompletableFuture,你可以使用 completedFuture 方法:

CompletableFuture<String> future = CompletableFuture.completedFuture("Hello, world!");

此外,你还可以使用 supplyAsync 方法来创建一个异步计算的 CompletableFuture

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 这里是一些长时间运行的计算
    return "Hello, world!";
});

1.2 处理 CompletableFuture 的结果

CompletableFuture 提供了一系列的方法来处理异步计算的结果。这些方法都返回一个新的 CompletableFuture,这样你可以将它们链接在一起形成一个处理管道。

例如,你可以使用 thenApply 方法来对结果进行转换:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
        .thenApply(s -> s + ", world!");

你还可以使用 thenAccept 方法来消费结果:

CompletableFuture.supplyAsync(() -> "Hello")
        .thenAccept(s -> System.out.println(s + ", world!"));

如果你不关心结果,只想在计算完成后执行一些操作,你可以使用 thenRun 方法:

CompletableFuture.supplyAsync(() -> "Hello")
        .thenRun(() -> System.out.println("Computation finished."));

1.3 组合 CompletableFuture

CompletableFuture 提供了一系列的方法来组合多个异步计算。

例如,你可以使用 thenCompose 方法来将两个异步计算串联起来:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
        .thenCompose(s -> CompletableFuture.supplyAsync(() -> s + ", world!"));

你还可以使用 thenCombine 方法来将两个异步计算并联起来:

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> ", world!");

CompletableFuture<String> future = future1.thenCombine(future2, String::concat);

如果你有多个 CompletableFuture,你可以使用 allOf 方法来等待它们全部完成:

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> ", world!");

CompletableFuture<Void> future = CompletableFuture.allOf(future1, future2);

1.4 处理 CompletableFuture 的异常

CompletableFuture 提供了两个方法来处理异常:exceptionally 和 handle

exceptionally 方法接受一个函数,这个函数将在计算抛出异常时被调用,它的返回值将作为新的结果:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    if (true) throw new RuntimeException("Exception!");
    return "Hello, world!";
}).exceptionally(ex -> "Sorry, we have an error!");

handle 方法和 exceptionally 类似,但是它可以处理正常的结果和异常:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    if (true) throw new RuntimeException("Exception!");
    return "Hello, world!";
}).handle((res, ex) -> {
    if (ex != null) {
        return "Sorry, we have an error!";
    } else {
        return res;
    }
});

以上就是 CompletableFuture 的主要用法。通过组合这些方法,你可以创建出复杂的异步处理管道,大大提高程序的性能和响应性。

二、使用 CompletableFuture 结合 MyBatis 和线程池批量插入数据

在处理大数据时,我们经常需要在数据库中插入大量的数据。如果我们使用传统的同步方法,可能会花费很长的时间。在这部分,我将展示如何使用 Java 的 CompletableFuture 进行异步处理,结合 MyBatis 和线程池来批量插入30万条数据,提高数据处理的效率。

2.1 创建线程池

首先,我们需要创建一个线程池,以便并发执行多个插入任务。我们可以使用 Java 的 Executors 类来创建线程池:

ExecutorService executor = Executors.newFixedThreadPool(10);

这将创建一个拥有10个线程的线程池。

2.2 创建数据

然后,我们需要创建要插入的数据。假设我们要插入一些用户数据,每个用户有一个名字和一个年龄:

class User {
    private String name;
    private int age;

    // getters and setters...
}

我们可以创建一个方法来生成用户数据:

List<User> generateUsers(int count) {
    List<User> users = new ArrayList<>();
    for (int i = 0; i < count; i++) {
        User user = new User();
        user.setName("User" + i);
        user.setAge(i % 100);
        users.add(user);
    }
    return users;
}

2.3 插入数据

接下来,我们需要创建一个方法来插入数据。我们将使用 MyBatis 的 SqlSession 来执行插入操作:

void insertUsers(SqlSession sqlSession, List<User> users) {
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    for (User user : users) {
        userMapper.insert(user);
    }
    sqlSession.commit();
}

注意,我们在插入所有用户后提交了事务。这是因为在大多数数据库中,事务提交是一个昂贵的操作,我们应该尽量减少事务提交的次数。

2.4 使用 CompletableFuture 插入数据

现在,我们可以使用 CompletableFuture 来并发插入数据。我们将数据分成多个批次,每个批次创建一个 CompletableFuture 来插入:

List<User> users = generateUsers(300000);
int batchSize = 1000;
List<CompletableFuture<Void>> futures = new ArrayList<>();

for (int i = 0; i < users.size(); i += batchSize) {
    List<User> batch = users.subList(i, Math.min(users.size(), i + batchSize));
    CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
            insertUsers(sqlSession, batch);
        }
    }, executor);
    futures.add(future);
}

CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

在上面的代码中,我们首先生成了300000个用户数据,然后将这些数据分成大小为1000的批次。对于每个批次,我们创建一个 CompletableFuture 来插入数据,然后将这个 CompletableFuture 添加到一个列表中。最后,我们使用 CompletableFuture.allOf 来等待所有的 CompletableFuture 完成。

2.5 关闭线程池

最后,我们需要关闭线程池。我们可以使用 ExecutorService.shutdown 方法来关闭线程池:

executor.shutdown();

这将等待所有已提交的任务完成,然后关闭线程池。

以上就是使用 CompletableFuture 结合 MyBatis 和线程池批量插入数据的方法。通过这种方式,我们可以大大提高插入数据的效率。希望这篇文章能帮助大家更好地理解和使用 CompletableFuture

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ABeam×Startup | 德硕管理咨询(深圳)创新研究团队拜访微漾创客空间

近日&#xff0c;德硕管理咨询&#xff08;深圳&#xff09;&#xff08;以下简称&#xff1a;“ABeam-SZ”&#xff09;创新研究团队前往微漾创客空间&#xff08;以下简称&#xff1a;微漾&#xff09;拜访参观&#xff0c;并展开合作交流。会议上&#xff0c;双方相互介绍了…

C语言 - 结构体、结构体数组、结构体指针和结构体嵌套

结构体的意义 问题&#xff1a;学籍管理需要每个学生的下列数据&#xff1a;学号、姓名、性别、年龄、分数&#xff0c;请用 C 语言程序存储并处理一组学生的学籍。 单个学生学籍的数据结构&#xff1a; 学号&#xff08;num&#xff09;&#xff1a; int 型姓名&#xff08;…

Dimensions网站——一个链接研究知识系统

Dimensions网站——一个链接研究知识系统 一、Dimensions网站简介 Dimensions 是一个链接的研究知识系统&#xff0c;它重新构想了发现和研究的获取。Dimensions 由 Digital Science 与全球 100 多个领先研究组织合作开发&#xff0c;汇集了资助、出版物、引文、替代指标、临…

如何在不使用任何软件的情况下将 PDF 转换为 Excel

通常&#xff0c;您可能会遇到这样的情况&#xff1a;您需要的数据不在 Excel 工作表中&#xff0c;而是以数据表形式出现在 PDF 文件中。为了将此数据放入 Excel 工作表中&#xff0c;如果您尝试将数字复制并粘贴到电子表格中&#xff0c;则列/行将无法正确复制和对齐。因此&a…

【数据结构】如何用栈实现队列?图文解析(LeetCode)

LeetCode链接&#xff1a;232. 用栈实现队列 - 力扣&#xff08;LeetCode&#xff09; 注&#xff1a;本文默认读者已掌握栈与队列的基本操作 可以看这篇文章熟悉知识点&#xff1a;【数据结构】栈与队列_字节连结的博客-CSDN博客 目录 做题思路 代码实现 1. MyQueue 2. …

选择排序:用C语言打造高效的排序算法

本篇博客会讲解如何使用C语言实现选择排序。 下面我来画图讲解选择排序的思路。 假设有一个数组&#xff0c;其初始状态如下&#xff0c;我们想把这个数组排成升序。 首先我们标明范围&#xff0c;即[begin, end]&#xff0c;一开始begin(b)和end(e)分别表示数组的第一个位置…

运维Shell脚本小试牛刀(一)

运维Shell脚本小试牛刀(一) 运维Shell脚本小试牛刀(二) 运维Shell脚本小试牛刀(三)::$(cd $(dirname $0)&#xff1b; pwd)命令详解 一: Shell中循环剖析 for 循环....... #!/bin/bash - # # # # FILE: countloop.sh # …

【ES】笔记-集合介绍与API

集合是一种不允许值重复的顺序数据结构。 通过集合我们可以进行并集、交集、差集等数学运算&#xff0c; 还会更深入的理解如何使用 ECMAScript 2015(ES2015)原生的 Set 类。 构建数据集合 集合是由一组无序且唯一(即不能重复)的项组成的。该数据结构使用了与有限集合相同的数…

文件修改时间能改吗?怎么改?

文件修改时间能改吗&#xff1f;怎么改&#xff1f;修改时间是每个电脑文件具备的一个属性&#xff0c;它代表了这个电脑文件最后一次的修改时间&#xff0c;是电脑系统自动赋予文件的&#xff0c;相信大家都应该知道。我们右击鼠标某个文件&#xff0c;然后点击弹出菜单里面的…

ELK安装、部署、调试(一)设计规划及准备

一、整体规划如图&#xff1a; 【filebeat】 需要收集日志的服务器&#xff0c;安装filebeat软件&#xff0c;用于收集日志。logstash也可以收集日志&#xff0c;但是占用的系统资源过大&#xff0c;所以使用了filebeat来收集日志。 【kafka】 接收filebeat的日志&#xff…

【Java笔记】分布式id生成-雪花算法

随着业务的增长&#xff0c;有些表可能要占用很大的物理存储空间&#xff0c;为了解决该问题&#xff0c;后期使用数据库分片技术。将一个数据库进行拆分&#xff0c;通过数据库中间件连接。如果数据库中该表选用ID自增策略&#xff0c;则可能产生重复的ID&#xff0c;此时应该…

【硬件设计】硬件学习笔记一--元器件的介绍与选型

硬件学习笔记一--元器件的选型 一、电阻1.1 电阻的分类1.2 电阻的选型 二、电容2.1 陶瓷电容2.2 钽电容2.3 铝电解电容2.4 电容选型 三、电感3.1 定义与介绍3.2 电感的分类3.3 电感的参数 四、磁珠4.1 磁珠的介绍4.2 磁珠的参数 五、二极管5.1 定义5.2 稳压管5.3 肖特基二极管5…

无涯教程-聚类算法 - K-Means

K-均值聚类算法计算质心并进行迭代&#xff0c;直到找到最佳质心为止&#xff0c;它假定群集的数目是已知的&#xff0c;它也称为扁平聚类算法。通过算法从数据中识别出的簇数以K均值中的" K"表示。 在该算法中&#xff0c;将数据点分配给群集&#xff0c;以使数据点…

五、Kafka消费者

目录 5.1 Kafka的消费方式5.2 Kafka 消费者工作流程1、总体流程2、消费者组原理3、消费者组初始化流程4、消费者组详细消费流程 5.3 消费者API1 独立消费者案例&#xff08;订阅主题&#xff09;2 独立消费者案例&#xff08;订阅分区&#xff09;3 消费者组案例 5.4 生产经验—…

Anolis 8.6 下 Redis 7.2.0 集群搭建和配置

Redis 7.2.0 搭建和集群配置 一.Redis 下载与单机部署1.Redis 下载2.虚拟机配置3.Redis 单机源码安装和测试4.Java 单机连接测试1.Pom 依赖2.配置文件3.启动类4.配置类5.单元测试6.测试结果 二.Redis 集群部署1.主从1.从节点配置2.Java 测试 2.哨兵1.哨兵节点配置2.复制一个哨兵…

eslint

什么是eslint ESLint 是一个根据方案识别并报告 ECMAScript/JavaScript 代码问题的工具&#xff0c;其目的是使代码风格更加一致并避免错误。 安装eslint npm init eslint/config执行后会有很多选项&#xff0c;按照自己的需求去选择就好&#xff0c;运行成功后会生成 .esli…

linux创建进程

linux创建进程 准备工作 准备工作 在Ubuntu64系统上 1、安装GCC和Make工具 编译器GCC&#xff1a;把C源码转为二进制程序 Make&#xff1a;自动编译多源文件项目 sudo apt-get update #更新存储库 sudo apt-get install build-essential #安装build-essential包 gcc --versio…

前端 js实现 选中数据 动态 添加在表格中

如下图展示&#xff0c;表格上方有属性内容&#xff0c;下拉选中后&#xff0c;根据选中的内容&#xff0c;添加在下方的表格中。 实现方式&#xff0c;&#xff08;要和后端约定&#xff0c;因为这些动态添加的字段都是后端返回的&#xff0c;后端自己会做处理&#xff0c…

〔019〕Stable Diffusion 之 单图中绘制多人分区域写提示词 篇

✨ 目录 &#x1f388; 下载区域绘制插件&#x1f388; 区域绘制使用&#x1f388; 参数讲解和基础使用&#x1f388; Lora 自组&#x1f388; Lora 自组的使用&#x1f388; 分区扩散&#x1f388; 分区域提示 &#x1f388; 下载区域绘制插件 在绘制图片时&#xff0c;经常绘…

【数据结构】带头双向循环链表---C语言版(单链表我们分手吧,不要再找我玩了!!!)

文章目录 &#x1f438;一、前言&#x1f438;二、链表的分类&#x1f344;1. 单向或者双向链表&#x1f344;2. 带头或者不带头链表&#x1f344;3. 循环或者非循环&#x1f344;4. 最常用链表 &#x1f438;三、带头双向循环链表详解&#x1f34e;创建带头双向循环链表⭕接口…