Java并发包中的CompletableFuture类使用详解

CompletableFuture 是 Java 8 引入的一个非常强大的类,它提供了一个可扩展的异步编程模型。CompletableFuture 表示一个可能还没有完成的异步计算,也就是说可以异步地执行任务,不阻塞当前线程,它提供了多种方法来组合和转换结果。

CompletableFuture 使用 thenApplythenAcceptthenRun 等方法,可以在计算完成时指定回调函数。

CompletableFuture 可以组合其他 CompletableFuture 对象,例如使用 thenCompose 方法。使用 exceptionally 方法可以处理异步操作中的异常。

CompletableFuture 的方法通常返回 CompletableFuture 类型,支持链式调用。

CompletableFuture 默认使用 ForkJoinPool 线程池,但可以通过 Executor 自定义线程池。

CompletableFuture 有多种完成阶段,如 completedfailed 和 cancelled

如果想要执行同步等待操作,使用 get 方法可以等待异步操作完成,但要注意的是,这可能引发的 InterruptedException异常。

以上是CompletableFuture的关键特性,下面我们来看一个业务场景案例,假设我们有一个场景,需要从数据库获取用户信息,然后根据用户信息调用外部服务进行处理,最后返回处理结果。

代码案例:

 

java

代码解读

复制代码

import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.function.Function; public class CompletableFutureExample { public static void main(String[] args) { // 模拟从数据库异步获取用户信息 CompletableFuture<User> userFuture = CompletableFuture.supplyAsync(() -> { // 模拟数据库查询 return getUserFromDatabase(); }); // 获取用户信息后,调用外部服务进行处理 CompletableFuture<String> processedResult = userFuture.thenApplyAsync(user -> { // 模拟外部服务处理 return processUserData(user); }); // 处理完成后,执行其他操作 processedResult.thenAccept(result -> { System.out.println("处理结果: " + result); // 可以继续链式调用其他操作 }); // 等待异步操作完成并获取结果(示例,实际使用中应避免在异步编程中使用) try { String finalResult = processedResult.get(); System.out.println("最终结果: " + finalResult); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } private static User getUserFromDatabase() { // 模拟数据库查询,返回用户信息 return new User("Vin", 30); } private static String processUserData(User user) { // 模拟外部服务处理用户数据 return "用户信息处理结果: " + user.getName() + ", 年龄: " + user.getAge(); } static class User { private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } } }

我们首先异步地从数据库获取用户信息,然后使用 thenApplyAsync 方法对用户信息进行处理,最后将处理结果输出到控制台。

这边整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

 需要全套面试笔记的【点击此处即可】免费获取

通过一个简单的例子,我们先感受了CompletableFuture类的功能,那CompletableFuture 在处理并发任务时有哪些优势和局限性呢?

先来看优势

  1. 链式调用: CompletableFuture 提供了丰富的方法,如 thenApplythenAcceptthenRun 等,支持链式调用,使得异步操作的编排变得简洁。

  2. 支持Java 8的函数式编程: 它支持Java 8的函数式编程特性,可以使用 Lambda 表达式作为参数,提高代码的可读性和简洁性。

  3. 错误处理: exceptionally 方法允许开发者为异步操作指定异常处理逻辑,使得错误处理更加集中和一致。

  4. 组合操作: 可以方便地组合多个 CompletableFuture 对象,例如使用 allOf 或 anyOf 来等待多个异步操作的完成。

  5. 非阻塞编程: CompletableFuture 支持非阻塞编程,可以在等待异步操作结果时继续执行其他任务。

CompletableFuture允许开发者自定义执行异步操作的线程池,提供了更好的控制力。并提供了多种API来处理异步操作,如 supplyAsyncrunAsync 等。相比传统的多线程同步方式,CompletableFuture 可以减少线程阻塞和上下文切换的开销。

再看局限性:

总结3点吧,在使用过程中需要注意一下:

  1. 过度使用可能导致性能问题: 如果过度使用 CompletableFuture 进行细粒度的异步操作,可能会导致过多的线程创建和上下文切换,反而降低性能。

  2. 阻塞调用: 虽然 CompletableFuture 支持非阻塞编程,但如果使用不当,例如使用 get 方法等待结果,可能会阻塞当前线程,失去异步的优势。

  3. 错误的使用方式: CompletableFuture 的一些方法,如 thenAcceptBoththenCombine 等,如果使用不当,可能会导致错误的逻辑处理。

CompletableFuture 是一个强大的工具,可以极大地简化异步编程的复杂性,但也需要开发者有良好的并发编程知识,以避免引入新的问题。

CompletableFuture 在处理并发任务时,如何避免资源泄露的问题?

避免资源泄露的问题

在Java中,资源泄露通常是指没有正确关闭那些占用系统资源的对象,如文件句柄、数据库连接、网络连接等。在使用CompletableFuture时,如果异步操作中使用了这些资源,但没有在操作完成后正确关闭它们,就可能导致资源泄露。

下面这个示例,咱们来演示可能导致资源泄露的CompletableFuture使用方式:

 

java

代码解读

复制代码

import java.io.Closeable; import java.io.IOException; import java.util.concurrent.CompletableFuture; class Resource implements Closeable { public Resource() { // 构造函数中初始化资源 } @Override public void close() throws IOException { // 清理和关闭资源 System.out.println("Resource closed"); } } public class ResourceLeakExample { public static void main(String[] args) { // 创建资源对象 Resource resource = new Resource(); // 使用CompletableFuture执行异步操作,但没有正确关闭资源 CompletableFuture<Void> future = CompletableFuture.runAsync(() -> { // 假设这里执行了一些操作 try { // 模拟长时间运行的任务 Thread.sleep(5000); System.out.println("Task completed"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // 没有调用resource.close(),导致资源泄露 }); // 没有等待异步操作完成并关闭资源 // future.join(); // 这行代码如果被注释掉,将不会等待异步任务完成 } }

在这个例子中,Resource 类代表了一个需要在使用后关闭的资源。在main方法中,我们创建了一个Resource对象,并在一个CompletableFuture的异步任务中使用它。然而,这个任务完成后并没有调用resource.close()来关闭资源。此外,main方法也没有等待异步任务完成(即使调用了future.join(),也不会调用close())。

这将导致以下问题:

  • Resource 对象没有被关闭,导致资源泄露。
  • 如果这个模式在整个应用程序中广泛存在,可能会导致系统资源耗尽。

为了避免资源泄露,我们应该确保在异步操作完成后关闭资源。这可以通过以下几种方式实现:

  • 使用try-with-resources语句确保自动关闭资源。
  • CompletableFuturewhenCompleteexceptionally方法中调用资源的关闭方法。
  • 使用finally块来关闭资源。

那要怎么办呢,我们尝试来修改一下,如何在异步操作完成后正确关闭资源:

 

java

代码解读

复制代码

public class ProperResourceManagementExample { public static void main(String[] args) { Resource resource = new Resource(); CompletableFuture<Void> future = CompletableFuture.runAsync(() -> { try { // 使用资源执行任务 Thread.sleep(5000); System.out.println("Task completed"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { try { resource.close(); } catch (IOException e) { e.printStackTrace(); } } }); // 等待异步操作完成 future.join(); } }

在这个修改后的示例中,我们在try块中使用资源,并在finally块中确保资源被关闭。这样即使发生异常,资源也会被正确关闭,从而避免了资源泄露。

小结一下

所以在使用 CompletableFuture 进行并发编程时,我们需要遵循一些实践来有效地避免资源泄露的问题: 在使用 CompletableFuture 进行并发编程时,遵循一些最佳实践可以有效地避免资源泄露:

  1. 使用 try-with-resources 语句: 确保在异步操作中使用的资源实现了 AutoCloseable 接口,并在 try-with-resources 语句中声明,以便它们可以自动关闭。

  2. 在 finallyApply 或 exceptionally 中关闭资源: 使用 finallyApply 方法来转换结果并在操作完成后关闭资源。如果操作失败,使用 exceptionally 方法来处理异常并关闭资源。

  3. 确保异步任务完成: 使用 join 或 get 方法等待异步任务完成,确保在应用程序关闭前所有任务都已完成。

  4. 避免资源在多处引用: 避免在异步操作中捕获外部资源的引用,这可能导致资源无法被垃圾收集器回收。

  5. 使用线程池: 自定义线程池时,确保线程池的大小适合应用程序的需求,并且正确管理线程池的生命周期。

  6. 合理配置线程池: 使用合理的线程池配置,例如设置最大线程数和队列容量,以避免资源耗尽。

  7. 监控资源使用情况: 使用适当的监控工具来跟踪资源使用情况,以便及时发现和解决资源泄露问题。

  8. 避免阻塞调用: 避免在异步操作链中使用阻塞调用,如 get 或 join,这可能会导致死锁或资源无法释放。

  9. 使用超时机制: 在调用 get 方法时,使用超时参数,以避免无限期地等待异步操作完成。

  10. 正确处理异常: 在异步操作链中使用 exceptionally 方法来捕获和处理异常,确保即使在发生异常时也能释放资源。

  11. 避免资源饥饿: 确保异步任务不会长时间占用资源,特别是在使用数据库连接或其他有限资源时。

  12. 使用资源池: 对于数据库连接、网络连接等资源,使用资源池来管理资源的分配和释放。

  13. 避免过度使用 CompletableFuture: 对于简单的同步操作,避免使用 CompletableFuture,以减少不必要的复杂性和资源消耗。

  14. 使用 CompletableFuture 的组合方法: 利用 CompletableFuture 提供的组合方法,如 thenCombinethenAcceptBoth 等,来避免创建不必要的中间对象。

以上这些,你可以在使用 CompletableFuture 进行并发编程时减少资源泄露的风险,并提高应用程序的稳定性和性能。

  • 11
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值