简体   繁体   English

SpringBoot-2.1.3:使用 @Async 和 CompletableFuture 的并行方法调用

[英]SpringBoot-2.1.3: Parallel Methods Invocation with @Async with CompletableFuture

Below is my code in which am trying to parallelize the 4 methods invocations since each method is independent of each other and performs some memory intensive statistical operations.下面是我的代码,我在其中尝试并行化 4 个方法调用,因为每个方法彼此独立并执行一些内存密集型统计操作。

@EnableAsync
@Configuration
public class Config {
   
    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();        
        executor.initialize();
        return executor;
    }
}

class OrderStatsService{

    public CumulativeStats compute() {
        log.info("CumulativeResult compute started at " + System.currentTimeMillis() + ", Current Thread Name: " + Thread.currentThread().getName() + ", Current Thread ID: " + Thread.currentThread().getId());
        List<Order> orders = getOrders();// API Call to fetch large set of orders size could be around 100k
        
        CumulativeResult cumulativeResult = new CumulativeResult();

        CompletableFuture<Long> stats1 = getStats1(orders);
        CompletableFuture<List<String>> result2 = getStats2(orders);
        CompletableFuture<Double> result3 = getStats3(orders);
        CompletableFuture<Map<String,String>> result4 = getStats4(orders);

        cumulativeResult.setStats1(stats1);
        cumulativeResult.setStats2(stats2);
        cumulativeResult.setStats3(stats3);
        cumulativeResult.setStats4(stats4);
        return cumulativeResult;
    }
    
    @Async("threadPoolTaskExecutor")
    public CompletableFuture<Long> getStats1(var orders) {
    log.info("getStats1 started at " + System.currentTimeMillis() + ", Current Thread Name: " + Thread.currentThread().getName() + ", Current Thread ID: " + Thread.currentThread().getId());
        //computes some stats
    }

    @Async("threadPoolTaskExecutor")
    public CompletableFuture<List<String>> getStats2(var orders) {
    log.info("getStats2 started at " + System.currentTimeMillis() + ", Current Thread Name: " + Thread.currentThread().getName() + ", Current Thread ID: " + Thread.currentThread().getId());
        //computes some stats
    }

    @Async("threadPoolTaskExecutor")
    public CompletableFuture<Double>  getStats3(var> orders) {
    log.info("getStats3 started at " + System.currentTimeMillis() + ", Current Thread Name: " + Thread.currentThread().getName() + ", Current Thread ID: " + Thread.currentThread().getId());
        //computes some stats
    }

    @Async("threadPoolTaskExecutor")
    public CompletableFuture<Map<String,String>> getStats4(var orders) {
    log.info("getStats4 started at " + System.currentTimeMillis() + ", Current Thread Name: " + Thread.currentThread().getName() + ", Current Thread ID: " + Thread.currentThread().getId());
        //computes some stats
    }

}

I'm getting the expected result but noticed that the main thread which is invoking the compute() is executing the other 4 methods getStats1 , getStats2 , getStats3 , getStats4 methods as well.我得到了预期的结果,但注意到调用compute()的主线程也在执行其他 4 个方法getStats1getStats2getStats3getStats4方法。

 CumulativeResult compute started at 1655783237437, Current Thread Name: http-nio-8080-exec-1, Current Thread ID: 28
 getStats1 started at 1655783238022, Current Thread Name: http-nio-8080-exec-1, Current Thread ID: 28
 getStats2 started at 1655783238024, Current Thread Name: http-nio-8080-exec-1, Current Thread ID: 28
 getStats3 started at 1655783463062, Current Thread Name: http-nio-8080-exec-1, Current Thread ID: 28
 getStats4 started at 1655783238085, Current Thread Name: http-nio-8080-exec-1, Current Thread ID: 28

I thought when we use CompletableFuture for @Async methods with @EnableAsync config those methods would be assigned a new thread for their execution, can someone please explain is this is the expected behavior or not for parallel methods invocation?我想当我们将CompletableFuture用于带有@EnableAsync配置的@Async方法时,这些方法将被分配一个新线程来执行它们,有人可以解释一下这是并行方法调用的预期行为吗? Is there anything wrong with my config?我的配置有什么问题吗? Or if this is the expected behavior how the parallelism is achieved when we are executing the caller method and the async in same thread?或者,如果这是我们在同一线程中执行caller方法和async时如何实现并行性的预期行为?

The required changes can be done in the code.可以在代码中进行所需的更改。

Step 1: The very first step to executing multithreading is to set up the thread pool config properly.第 1 步:执行多线程的第一步是正确设置线程池配置。

Here is an example of how you can set it up the size of the thread这是一个如何设置线程大小的示例

@Configuration
@EnableAsync
public class ThreadPoolConfiguration {

@Bean(name = "taskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
    ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
    threadPoolTaskExecutor.setThreadNamePrefix("thread-");
    threadPoolTaskExecutor.setCorePoolSize(100);
    threadPoolTaskExecutor.setMaxPoolSize(120);
    threadPoolTaskExecutor.setQueueCapacity(100000);
    threadPoolTaskExecutor.initialize();
    return threadPoolTaskExecutor;
}

Step 2: Using @Async annotation第二步:使用@Async注解

First, let's go over the rules.首先,让我们回顾一下规则。

  • @Async annotation - It must be applied to public methods only. @Async 注释 - 它只能应用于公共方法。
  • Self-invocation for @Async will not work which means calling the async method from within the same class will not work. @Async 的自调用将不起作用,这意味着从同一个类中调用异步方法将不起作用。

For more information on @Async you can go through有关@Async 的更多信息,您可以通过

Solutions :解决方案:

  • Keep all the methods with @Async in different service and you can call it from OrderStatsService将 @Async 的所有方法保存在不同的服务中,您可以从OrderStatsService调用它
  • One more thing you can do is call from the controller and all the @Async methods can be kept in the service class.您可以做的另一件事是从控制器调用,所有 @Async 方法都可以保存在服务类中。
  • Mark you annotation with the Bean name like @Async("taskExecutor")@Async("taskExecutor")之类的 Bean 名称标记您的注释

Either of the ways will work for you.任何一种方式都适合你。

You should set thread pool size for controlling thread count:您应该设置线程池大小以控制线程数:

final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("CarThread-");

Multi-Threading in Spring Boot Using CompletableFuture Spring Boot 中的多线程使用 CompletableFuture

I would recommend checking some facts:我建议检查一些事实:

  1. Is your OrderStatsService class static and is it a Bean managed by Spring?您的OrderStatsService类是static的吗?它是由 Spring 管理的Bean吗?
  2. @Async methods of a Bean have to be invoked directly from it's caller. Bean@Async方法必须直接从它的调用者调用。 What you are having in your OrderStatsService ( Bean -if you've done this already) is calling the non-@Async method, followed by invoking the @Async methods.您在OrderStatsServiceBean -如果您已经这样做了)中拥有的是调用non-@Async @Async 方法,然后调用@Async方法。 In my experience, this won't give you the expected behaviour.以我的经验,这不会给你预期的行为。

Feel free to correct me if I'm being a dummy dumb dumb.如果我是一个愚蠢的笨蛋,请随时纠正我。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用 CompletableFuture 异步调用 FeignClient Springboot - Async call of a FeignClient Springboot with CompletableFuture 何时使用CompletableFuture的非异步方法? - When to use non-async methods of CompletableFuture? Spring Async、Async with CompletableFuture 和 Java8 并行 stream 之间的区别 - Difference between Spring Async, Async with CompletableFuture and Java8 parallel stream 我应该如何在 springboot async 中使用 CompletableFuture 作为 void 方法 - How should I use CompletableFuture for void method in springboot async 从异步方法的同步部分抛出异常,返回CompletableFuture - Throwing exceptions from sync portions of async methods returning CompletableFuture Java8 - CompletableFuture - 在异步调用中按顺序运行方法 - Java8 - CompletableFuture - Running methods in async call sequentially Java 8 - 并行调用方法异步并结合它们的结果 - Java 8 - Call methods async in parallel and combine their results CompletableFuture VS @Async - CompletableFuture VS @Async Java 8 - 如何使用 CompletableFuture 跟踪异步并行流中调用的异常数 - Java 8 - How to track number of exceptions invoked within an async parallel stream using CompletableFuture 从 SpringBoot 2.1.3.RELEASE 发送邮件 - sending email from SpringBoot 2.1.3.RELEASE
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM