簡體   English   中英

SpringBoot-2.1.3:使用 @Async 和 CompletableFuture 的並行方法調用

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

下面是我的代碼,我在其中嘗試並行化 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
    }

}

我得到了預期的結果,但注意到調用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

我想當我們將CompletableFuture用於帶有@EnableAsync配置的@Async方法時,這些方法將被分配一個新線程來執行它們,有人可以解釋一下這是並行方法調用的預期行為嗎? 我的配置有什么問題嗎? 或者,如果這是我們在同一線程中執行caller方法和async時如何實現並行性的預期行為?

可以在代碼中進行所需的更改。

第 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;
}

第二步:使用@Async注解

首先,讓我們回顧一下規則。

  • @Async 注釋 - 它只能應用於公共方法。
  • @Async 的自調用將不起作用,這意味着從同一個類中調用異步方法將不起作用。

有關@Async 的更多信息,您可以通過

解決方案:

  • 將 @Async 的所有方法保存在不同的服務中,您可以從OrderStatsService調用它
  • 您可以做的另一件事是從控制器調用,所有 @Async 方法都可以保存在服務類中。
  • @Async("taskExecutor")之類的 Bean 名稱標記您的注釋

任何一種方式都適合你。

您應該設置線程池大小以控制線程數:

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

Spring Boot 中的多線程使用 CompletableFuture

我建議檢查一些事實:

  1. 您的OrderStatsService類是static的嗎?它是由 Spring 管理的Bean嗎?
  2. Bean@Async方法必須直接從它的調用者調用。 您在OrderStatsServiceBean -如果您已經這樣做了)中擁有的是調用non-@Async @Async 方法,然后調用@Async方法。 以我的經驗,這不會給你預期的行為。

如果我是一個愚蠢的笨蛋,請隨時糾正我。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM