SpringBoot-2.1.3: Parallel Methods Invocation with @Async with CompletableFuture

Ran*_*aul 2 java parallel-processing multithreading stream spring-boot

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.

@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
    }

}
Run Code Online (Sandbox Code Playgroud)

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.

 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
Run Code Online (Sandbox Code Playgroud)

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? 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?

Ash*_*ale 7

所需的更改可以在代码中完成。

步骤 1:执行多线程的第一步是正确设置线程池配置。

这是如何设置线程大小的示例

@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;
}
Run Code Online (Sandbox Code Playgroud)

第2步:使用@Async注解

首先,让我们回顾一下规则。

  • @Async 注释 - 它必须仅应用于公共方法。
  • @Async 的自调用将不起作用,这意味着从同一个类中调用异步方法将不起作用。

有关@Async的更多信息,您可以通过

解决方案:

  • 将所有带有 @Async 的方法保留在不同的服务中,您可以从OrderStatsService调用它
  • 您还可以做的另一件事是从控制器调用,并且所有 @Async 方法都可以保留在服务类中。
  • 使用 Bean 名称标记您的注释,例如@Async("taskExecutor")

任何一种方法都适合您。