写点什么

CompletableFuture 使用指南

作者:FunTester
  • 2024-06-17
    河北
  • 本文字数:3860 字

    阅读完需:约 13 分钟

在 Java 并发编程中,传统的线程和同步机制如Thread类和Runnable接口提供了基本的并行执行能力,但它们的使用往往需要编写大量的样板代码来处理线程的创建、管理和同步,从而导致代码复杂且难以维护。为了解决这些问题,Java 5 引入了java.util.concurrent包,提供了如ExecutorServiceFuture等高级抽象来简化并发编程。然而,Future接口在处理异步任务时仍然存在一些局限,例如无法方便地处理回调、组合多个任务以及处理异常。


为了解决这些问题,Java 8 引入了CompletableFuture,它不仅实现了Future接口,还提供了丰富的 API 来支持异步编程。通过CompletableFuture,开发者可以更优雅地处理异步任务的执行、结果处理和异常处理。CompletableFuture提供了诸如thenApplythenAcceptthenCombine等方法,可以轻松地将多个异步任务串联或并行执行,并在任务完成后进行回调处理。此外,CompletableFuture还支持自定义线程池,使得开发者可以灵活地管理线程资源,提高程序的并发性能和可维护性。


CompletableFuture的引入极大地简化了 Java 并发编程,提供了一种更直观、更强大的方式来编写异步和并行代码,使得复杂的并发任务变得更加易于实现和维护。

功能

CompletableFuture专注于异步任务的结果,并提供丰富的 API 用于组合和错误处理。它负责:


  • 并行处理:可以将多个独立的任务并行执行,然后合并结果。

  • 异步回调:可以在任务完成后执行回调函数,而不阻塞主线程。

  • 异常处理:在异步操作中更方便地处理异常情况。

代码示例

以下代码演示了在 Java 中使用来CompletableFuture处理异步计算。


public static void main(String[] args) {      CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {          System.out.println("Hello,FunTester! " + Thread.currentThread().getName());          return "Hello,FunTester!";      });      future.thenAccept(System.out::println);      future.join();  }
复制代码


这个示例代码展示了如何使用 Java 的 CompletableFuture 类来异步执行任务,并处理任务的结果。让我们逐步解析一下:


  1. CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {...});这一行创建了一个 CompletableFuture 实例,并使用supplyAsync方法异步执行提供的 lambda 表达式。lambda 表达式的代码块中,首先打印了一个字符串和当前线程名称,然后返回字符串"Hello,FunTester!"

  2. future.thenAccept(System.out::println);这一行注册了一个回调函数,当上一步异步任务完成时,它会将任务的结果(即字符串"Hello,FunTester!"传递给System.out::println方法,从而将其打印到控制台。

  3. future.join();这一行是一个阻塞操作,它会等待异步任务完成。如果异步任务已经完成,则立即返回;否则,它会一直等待直到异步任务完成。


因此,运行这个程序时,它会先打印"Hello,FunTester! [线程名称]"(这是在异步任务中打印的),然后打印"Hello,FunTester!"(这是由thenAccept回调打印的)。


这个示例展示了 CompletableFuture 如何简化异步编程。你可以使用 lambda 表达式来定义异步任务,并使用thenAccept等方法来注册对任务结果的处理逻辑。CompletableFuture 还提供了其他有用的方法,如thenApplythenCompose等,用于组合和链式执行多个异步任务。

链式异步任务

CompletableFuture的强大功能之一就是能够将多个异步任务链接在一起。处理复杂的异步工作流时,这可以使代码更具可读性和可维护性。


以下代码演示了如何CompletableFuture在 Java 中使用链接多个任务来创建一系列异步计算。


import java.util.concurrent.CompletableFuture; public class ChainingTasksExample {    public static void main(String[] args) {        CompletableFuture.supplyAsync(() -> "Task 1")            .thenApply(result -> result + " + Task 2")            .thenApply(result -> result + " + Task 3")            .thenAccept(System.out::println);    }}
复制代码


这个案例中展示了 CompletableFuture 的链式调用和结果转换的用法。让我们逐步解析一下:


  1. CompletableFuture.supplyAsync(() -> "Task 1")

  2. 这一行创建了一个 CompletableFuture 实例,并使用supplyAsync方法异步执行一个 lambda 表达式,该表达式返回字符串"Task 1"

  3. .thenApply(result -> result + " + Task 2")

  4. thenApply方法接受一个函数式接口Function作为参数,该函数接收上一个任务的结果作为输入,并返回一个新的结果。

  5. 在这里,lambda 表达式result -> result + " + Task 2"将上一个任务的结果("Task 1")与字符串" + Task 2"连接,返回"Task 1 + Task 2"

  6. .thenApply(result -> result + " + Task 3")

  7. 这一行又使用thenApply方法,将上一个任务的结果("Task 1 + Task 2")与字符串" + Task 3"连接,返回"Task 1 + Task 2 + Task 3"

  8. .thenAccept(System.out::println);

  9. thenAccept方法接受一个函数式接口Consumer作为参数,该接口消费上一个任务的结果,但不返回任何值。

  10. 在这里,使用System.out::println方法引用作为Consumer的实现,它将打印上一个任务的结果("Task 1 + Task 2 + Task 3")。


因此,当你运行这个代码时,它会异步执行三个任务,每个任务在上一个任务的结果上追加一个字符串。最终,它会将最终的结果"Task 1 + Task 2 + Task 3"打印到控制台。


这个示例展示了 CompletableFuture 如何通过链式调用和结果转换来组合多个异步任务。每个thenApply方法都会在上一个任务完成后异步执行,并将结果传递给下一个任务。最后,thenAccept方法用于消费最终的结果

错误处理

CompletableFuture提供了多种方法来处理异步任务执行过程中发生的异常。您可以使用exceptionallyhandle和等方法whenComplete来妥善处理错误。


以下代码演示了在使用CompletableFutureJava 时如何正确处理错误。


import java.util.concurrent.CompletableFuture; public class ErrorHandlingExample {    public static void main(String[] args) {        CompletableFuture.supplyAsync(() -> {            if (true) throw new RuntimeException("Something went wrong!");            return "Success";        }).exceptionally(ex -> {            System.out.println("Error: " + ex.getMessage());            return "Fallback result";        }).thenAccept(System.out::println);    }}
复制代码

超时管理

在异步编程中,管理超时至关重要,以避免无限期地等待任务完成。提供和CompletableFuture等方法来有效地处理超时。


以下代码演示了如何CompletableFuture在 Java 中管理超时。


import java.util.concurrent.CompletableFuture;import java.util.concurrent.TimeUnit; public class TimeoutManagementExample {    public static void main(String[] args) {        CompletableFuture.supplyAsync(() -> {                    try {                        TimeUnit.SECONDS.sleep(5);                    } catch (InterruptedException e) {                        throw new IllegalStateException(e);                    }                    return "Result after delay";                }).orTimeout(2, TimeUnit.SECONDS)                .exceptionally(ex -> "Timeout occurred")                .thenAccept(System.out::println);    }}
复制代码


这个例子演示了如何使用CompletableFutureorTimeout方法来设置异步任务的超时时间,以及如何在超时发生时进行处理。让我们逐步分析一下:


  1. CompletableFuture.supplyAsync(() -> { ... })

  2. 这一行创建了一个 CompletableFuture 实例,并使用supplyAsync方法异步执行提供的 lambda 表达式。

  3. 在该 lambda 表达式中,代码调用TimeUnit.SECONDS.sleep(5)故意让任务休眠 5 秒钟,模拟一个耗时操作。

  4. .orTimeout(2, TimeUnit.SECONDS)

  5. orTimeout方法设置了异步任务的超时时间为 2 秒。如果任务在 2 秒内未完成,则会触发超时并返回一个TimeoutException

  6. .exceptionally(ex -> "Timeout occurred")

  7. exceptionally方法接受一个函数式接口Function作为参数,该函数接收异步任务抛出的异常作为输入,并返回一个备用结果。

  8. 在这里,lambda 表达式ex -> "Timeout occurred"接收到异常实例ex后,返回字符串"Timeout occurred"作为备用结果。

  9. .thenAccept(System.out::println);

  10. thenAccept方法接受一个函数式接口Consumer作为参数,该接口消费上一个任务的结果,但不返回任何值。

  11. 在这里,使用System.out::println方法引用作为Consumer的实现,它将打印上一个任务的结果(即备用结果"Timeout occurred"或成功结果"Result after delay"(如果任务在 2 秒内完成))。


当我们运行这个程序时,由于异步任务会休眠 5 秒钟,而超时时间设置为 2 秒钟,因此会触发超时。exceptionally方法会被调用,并返回备用结果"Timeout occurred"thenAccept方法,最终被打印到控制台。


输出应该是:


Timeout occurred
复制代码


如果将超时时间设置为大于 5 秒,例如orTimeout(6, TimeUnit.SECONDS),那么输出将是:


Result after delay
复制代码


这个示例展示了如何使用orTimeout方法来设置 CompletableFuture 的超时时间,以及如何使用exceptionally方法来处理超时情况。在一些需要控制任务执行时间的场景中,这个功能非常有用,可以防止任务无限期地阻塞或占用资源。

结论

JavaExecutorServiceCompletableFuture是管理现代应用程序中并发性的强大工具。它们通过提供易于使用的任务管理、链接、错误处理和超时管理 API 来简化异步编程的复杂性。通过理解和利用这些实用程序,开发人员可以编写高效、响应迅速且易于维护的并发应用程序。

发布于: 刚刚阅读数: 2
用户头像

FunTester

关注

公众号:FunTester,800篇原创,欢迎关注 2020-10-20 加入

Fun·BUG挖掘机·性能征服者·头顶锅盖·Tester

评论

发布
暂无评论
CompletableFuture 使用指南_FunTester_InfoQ写作社区