Java CompletableFuture 详细使用教程与实践

72 篇文章 1 订阅
4 篇文章 0 订阅

一、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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CompletableFuture.allOf方法用于等待多个CompletableFuture完成,并返回一个新CompletableFuture,该CompletableFuture在所有输入CompletableFuture完成后完成。它不会返回任何结果,只是用于等待所有的CompletableFuture完成。 下面是一个使用CompletableFuture.allOf的示例代码[^2]: ```java import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class CompletableFutureExample { public static void main(String[] args) { CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } return "Result from Future 1"; }); CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } return "Result from Future 2"; }); CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "Result from Future 3"; }); CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2, future3); try { allFutures.get(); System.out.println("All futures completed"); System.out.println("Result from Future 1: " + future1.get()); System.out.println("Result from Future 2: " + future2.get()); System.out.println("Result from Future 3: " + future3.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } } ``` 在上面的示例中,我们创建了三个CompletableFuture对象,每个对象都模拟了一个耗时的操作。然后,我们使用CompletableFuture.allOf方法等待所有的CompletableFuture完成。最后,我们通过调用get方法来获取每个CompletableFuture的结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值