优雅的并发编程

编程入门 行业动态 更新时间:2024-10-25 06:20:52

<a href=https://www.elefans.com/category/jswz/34/1770208.html style=优雅的并发编程"/>

优雅的并发编程

目录

了解CompletableFuture

CompletableFuture 是 Java 8 引入的一个类,用于支持异步编程和非阻塞操作。它提供了一种简单而强大的方式来处理异步任务,可以轻松地实现并行、非阻塞的操作,并且提供了丰富的方法来处理任务的完成状态、异常情况以及多个任务之间的串联和组合。

CompletableFuture使用场景

  1. 并行处理多个独立任务:当一个任务可以被分解为多个独立的子任务时,可以使用CompletableFuture来并行执行这些子任务,从而提高系统的性能和响应速度。比如,在电商系统中,查询用户信息、订单信息、购物车信息等可以并行执行,然后在所有子任务完成后进行结果合并。
  2. 异步执行耗时操作:对于一些耗时的操作,比如远程调用、数据库查询等,可以使用CompletableFuture来异步执行这些操作,避免阻塞主线程,提高系统的吞吐量和并发能力。
  3. 组合多个异步任务的结果:有时候需要等待多个异步任务都完成后才能进行下一步处理,可以使用CompletableFuture的组合方法(比如thenCombine、thenCompose等)来等待多个异步任务的结果,并在所有任务完成后进行处理。
  4. 超时处理和异常处理:CompletableFuture提供了丰富的异常处理和超时处理的方法,可以很方便地处理异步任务执行过程中出现的异常或者超时情况。
  5. 实现异步回调:通过CompletableFuture的回调方法,可以在异步任务完成后执行特定的逻辑,比如通知其他系统、记录日志等。

多线程任务使用案例

我将使用CompletableFuture的以下API来说明CompletableFuture的使用方法。

  • supplyAsync:启动一个异步任务,并返回一个CompletableFuture对象,该任务会在传入的Supplier中执行。在这个例子中,第一个CompletableFuture启动了一个异步任务,模拟了一个耗时的操作,然后返回一个整数结果。
  • exceptionally:处理上一个阶段执行过程中出现的异常,返回一个默认值。这里的result2使用exceptionally方法来处理result1阶段的异常,如果result1出现异常,就返回一个包含异常信息的字符串。
  • thenApply:对上一个阶段的结果进行处理,并返回一个新的CompletableFuture对象。在这个例子中,result1是通过对future1的结果进行处理得到的,将异步任务1的结果转换为字符串并添加额外的处理。
  • exceptionally:处理上一个阶段执行过程中出现的异常,返回一个默认值。这里的result2使用exceptionally方法来处理result1阶段的异常,如果result1出现异常,就返回一个包含异常信息的字符串。
  • orTimeout:设置异步任务的超时时间。在这个例子中,result3使用orTimeout方法来设置任务的超时时间为2分钟,如果任务未在指定时间内完成,将抛出TimeoutException。
  • runAsync:启动一个异步任务,但不返回任何结果。在这个例子中,result4表示一个没有返回值的异步任务,只是简单地打印一条信息。
  • allOf:等待所有的CompletableFuture执行完毕。在这个例子中,通过使用allOf方法,等待result2、result3和result4都完成后再继续执行后续的逻辑。
  • join:等待CompletableFuture的完成并获取结果。在这个例子中,通过调用join方法等待所有任务完成,并获取它们的结果。

API使用案例

public class CompletableFutureExample {public static void main(String[] args) throws ExecutionException, InterruptedException {// 异步任务启动:supplyAsyncCompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {System.out.println("异步任务1开始执行");try {//模拟执行耗时操作TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new IllegalStateException(e);}return 100;});// 异步任务结果处理:thenApplyCompletableFuture<String> result1 = future1.thenApply(value -> {System.out.println("任务1结果处理: " + value);return "Processed Result1: " + value;});// 串联/组合多个任务的处理结果:exceptionallyCompletableFuture<String> result2 = result1.exceptionally(ex -> "任务1出现异常: " + ex.getMessage());// 超时处理:orTimeoutCompletableFuture<String> result3 = CompletableFuture.supplyAsync(() -> {try {//模拟执行耗时操作TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new IllegalStateException(e);}return "任务2完成";}).orTimeout(2, TimeUnit.MINUTES);// 流处理/函数式编程:与 Stream 接口结合CompletableFuture<Void> result4 = CompletableFuture.runAsync(() -> {System.out.println("任务3开始执行");});// 等待所有任务完成CompletableFuture.allOf(result2, result3, result4).join();System.out.println(result2.get());System.out.println(result3.get());}
}

业务优化案例

假如你是一名高级高级工程师,你们项目组正在做电商相关的业务,对接口的响应速度要求较高,现在有个需求是需要同时查询出:用户的账号信息、订单信息、购物车信息、银行卡支付信息,如果没有并发思想,那么接口的视线逻辑大致是这样的:

public class ECommerceService {public UserAccount getUserAccount() {// 执行查询用户账号信息的逻辑UserAccount userAccount = // ...;return userAccount;}public OrderInfo getOrderInfo() {// 执行查询订单信息的逻辑OrderInfo orderInfo = // ...;return orderInfo;}public ShoppingCart getShoppingCart() {// 执行查询购物车信息的逻辑ShoppingCart shoppingCart = // ...;return shoppingCart;}public BankCardPaymentInfo getBankCardPaymentInfo() {// 执行查询银行卡支付信息的逻辑BankCardPaymentInfo bankCardPaymentInfo = // ...;return bankCardPaymentInfo;}public void processECommerceRequest() {UserAccount userAccount = getUserAccount();OrderInfo orderInfo = getOrderInfo();ShoppingCart shoppingCart = getShoppingCart();BankCardPaymentInfo bankCardPaymentInfo = getBankCardPaymentInfo();// 在这里合并处理各个查询结果// ...}
}

以上是接口串行执行的逻辑,当前服务器环境一般都是多核心、多线程,服务器可以同时处理多个任务,如果此时还使用单线程去执行,有些“暴殄天物”,并且接口响应速度会比较慢,那么优化的方式就是开启多个线程去分别执行不同的逻辑,那么我们可以使用CompletableFuture来优化代码,提高接口响应速度。

import java.util.concurrent.CompletableFuture;public class ECommerceService {public CompletableFuture<UserAccount> getUserAccountAsync() {return CompletableFuture.supplyAsync(() -> {// 执行查询用户账号信息的逻辑UserAccount userAccount = // ...;return userAccount;});}public CompletableFuture<OrderInfo> getOrderInfoAsync() {return CompletableFuture.supplyAsync(() -> {// 执行查询订单信息的逻辑OrderInfo orderInfo = // ...;return orderInfo;});}public CompletableFuture<ShoppingCart> getShoppingCartAsync() {return CompletableFuture.supplyAsync(() -> {// 执行查询购物车信息的逻辑ShoppingCart shoppingCart = // ...;return shoppingCart;});}public CompletableFuture<BankCardPaymentInfo> getBankCardPaymentInfoAsync() {return CompletableFuture.supplyAsync(() -> {// 执行查询银行卡支付信息的逻辑BankCardPaymentInfo bankCardPaymentInfo = // ...;return bankCardPaymentInfo;});}public CompletableFuture<Void> processECommerceRequest() {CompletableFuture<UserAccount> userAccountFuture = getUserAccountAsync();CompletableFuture<OrderInfo> orderInfoFuture = getOrderInfoAsync();CompletableFuture<ShoppingCart> shoppingCartFuture = getShoppingCartAsync();CompletableFuture<BankCardPaymentInfo> bankCardPaymentInfoFuture = getBankCardPaymentInfoAsync();return CompletableFuture.allOf(userAccountFuture, orderInfoFuture, shoppingCartFuture, bankCardPaymentInfoFuture).thenAcceptAsync((Void) -> {UserAccount userAccount = userAccountFuture.join();OrderInfo orderInfo = orderInfoFuture.join();ShoppingCart shoppingCart = shoppingCartFuture.join();BankCardPaymentInfo bankCardPaymentInfo = bankCardPaymentInfoFuture.join();// 在这里合并处理各个异步任务的结果// ...});}
}

以上是账号、订单、购物车、银行卡支付逻辑块各开启一个线程异步去执行,最后等待各个线程执行完毕汇总结果,提高接口影响速度。

单线程和多线程相比,举个不恰当的例子:当你为了报仇,踌躇满志上山叩拜师门,经过十年寒窗苦练、不舍昼夜的练习,终于学成归来下山复仇,结果仇家拿出AK 47一阵突突,云淡风轻的跟你说:“大人,时代变了”的无力感。

一点小提示

问题

CompletableFuture在线程池中执行时,可能会出现代码异常,但是并没有将异常抛出的情况,原因有二:

  1. CompletableFuture的异步任务中,如果出现异常而没有显式地处理或抛出,那么这个异常就会被吞噬,不会传播到CompletableFuture的最终结果上。
  2. 另一种可能是因为异步任务在执行时发生了异常,但是在后续对CompletableFuture的处理过程中,没有正确处理这些异常。例如,在使用thenApplyexceptionally等方法对CompletableFuture的结果进行处理时,没有考虑到可能存在的异常情况,导致异常被掩盖。

解决方案

为了解决这个问题,你可以在异步任务的代码中,适当地处理异常并进行抛出,或者使用exceptionally方法来处理异常情况,确保异常能够正确地传播并得到处理。此外,在对CompletableFuture的结果进行处理时,需要注意处理可能发生的异常情况,以确保异常能够被及时捕获和处理。

CompletableFuture优缺点分析

CompletableFuture 是 Java 8 开始引入的一个用于支持异步编程的工具类,它提供了丰富的 API 来简化异步编程,并提供了对多个异步操作的组合、串行和并行执行的支持。下面是 CompletableFuture 的一些优缺点分析:

优点:

  1. 简化异步编程CompletableFuture 提供了简洁的 API,使得异步编程变得更加直观和易于理解。它允许开发者在不同的阶段对异步任务的结果进行处理、转换,甚至是以函数式编程的方式进行流式处理。
  2. 异步任务组合CompletableFuture 支持多个异步任务的组合、串行和并行执行,可以通过 thenComposethenCombine 等方法灵活地组织复杂的异步任务流程。
  3. 异常处理CompletableFuture 提供了异常处理机制,可以通过 exceptionally 方法对异步任务执行过程中的异常进行处理,从而保证异常能够被正确捕获和处理。
  4. 超时处理CompletableFuture 支持设置异步任务的超时时间,可以通过 orTimeout 方法指定超时时间,防止任务长时间执行导致阻塞。
  5. 与函数式编程结合CompletableFuture 运用了函数式编程的思想,使得异步任务的处理更加灵活,并且可以与 Stream 接口等其他函数式编程工具结合使用。

缺点:

  1. 学习曲线:对于初学者来说,CompletableFuture 的 API 可能会有一定的学习曲线,特别是对于那些不熟悉函数式编程和异步编程模型的开发者来说。
  2. 过度使用复杂性:在一些简单的场景下,使用 CompletableFuture 可能会显得过于复杂,特别是在一些简单的线性任务处理中,可能会显得比较繁琐。
  3. 调试困难:由于 CompletableFuture 支持异步任务的组合和串行/并行执行,当出现逻辑错误或异常时,可能需要仔细追踪 CompletableFuture 链中的每个环节,以确定问题的所在,这可能会增加调试的难度。

综上所述,CompletableFuture 作为 Java 异步编程的利器,提供了丰富的功能和灵活的操作方式,但在使用时需要根据实际情况权衡其优缺点,避免过度复杂化简单的任务处理,并合理处理异常情况以及确保代码的可读性和可维护性。


关于CompletableFuture的相关实现原理,请您留个关注,明日更新。

感谢您看到最后,希望能解决您的困扰和疑惑,这是我更新最大的动力。

关于我

👋🏻你好,我是Debug.c。微信公众号:种颗代码技术树 的维护者,一个跨专业自学Java,对技术保持热爱的bug猿,同样也是在某二线城市打拼四年余的Java Coder。

🏆在掘金、CSDN、公众号我将分享我最近学习的内容、踩过的坑以及自己对技术的理解。

📞如果您对我感兴趣,请联系我。

若有收获,就点个赞吧

更多推荐

优雅的并发编程

本文发布于:2023-11-15 22:30:57,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1607633.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:优雅

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!