代码之家  ›  专栏  ›  技术社区  ›  Andrew Mairose

异步方法调用完成后,Spring命令行应用程序挂起

  •  3
  • Andrew Mairose  · 技术社区  · 8 年前

    我有一个Spring Boot应用程序 CommandLineRunner 还有春天 @Async 注释以异步运行方法。这一切都很好,但当我的所有线程完成时,应用程序只是挂起而不是退出。

    下面是我的应用程序中的一个最小示例:

    应用Java语言

    @SpringBootApplication
    @EnableAsync
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    

    应用程序启动。Java语言 :

    @Component
    public class ApplicationStartup implements CommandLineRunner {
    
        private final AsyncService asyncService;
    
        @Inject
        public ApplicationStartup(final AsyncService asyncService) {
            this.asyncService = asyncService;
        }
    
        @Override
        public void run(final String... strings) throws Exception {
            //my logic is more complicated than this, but this illustrates my point
            for (int i = 0; i < 1000; i++) {
                asyncService.runAsyncMethod();
            }
        }
    }
    

    异步服务。Java语言 :

    @Service
    @Transactional
    public class AsyncService {
    
        @Async
        public void runAsyncMethod() {
            //perform call to an API and process results
        }
    
    }
    

    ExecutorConfig。Java语言 :

    @Configuration
    public class ExecutorConfig() {
        @Bean
        public ThreadPoolTaskExecutor asyncExecutor() {
            final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(64);
            executor.setMaxPoolSize(64);
            executor.setQueueCapacity(500);
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            executor.setThreadNamePrefix("Scrub-");
            executor.setKeepAliveSeconds(60);
            executor.initialize();
            return executor;
        }
    }
    

    我的所有线程都调用 runAsyncMethod() 每个方法调用都成功完成,但应用程序只是挂起。

    我试着改变一些执行器的设置。我没有 keepAliveSeconds 起初,我认为添加它可以修复它,但在所有线程完成后,它仍然挂起。我变了 corePoolSize 到 0 ,这使得应用程序在完成时退出,但整个过程只使用了1个线程。

    2 回复  |  直到 8 年前
        1
  •  3
  •   xerx593    6 年前

    参加 异步作业,这就是为什么 run 方法在所有线程完成之前退出(far),尴尬的行为“更容易理解”。

    doc ,您可以像这样加入:

    ...
    CompletableFuture<Void>[] myJobs = new CompletableFuture<>[N];
    ...
    for (int i = 0; i < N; i++) {
            myJobs[i] = asyncService.runAsyncMethod();
    }
    ...
    CompletableFuture.allOf(myJobs).join();
    

    还有你的 runAsyncMethod() 需要返回一个 CompletableFuture<Void> . 要做到这一点,你可以 return CompletableFuture.completedFuture(null);

        2
  •  2
  •   msangel    6 年前

    即使 marked as correct 答案有效。这不是完整的答案。

    没有 @EnableAsync 没有WEB环境 .web(WebApplicationType.NONE) spring boot应用程序启动后会自动停止(因为没有什么要做/等待的)。所以即使你不这么做 apringApp.close() 在你的应用程序中,但仅限于 app.run(commandLine) 这个 .close() 方法自动调用。

    但一旦你添加 @启用同步 -行为会发生变化,因为可能存在异步工作,所以应用程序在启动后不会停止。如果没有停止代码,应用程序将保持工作状态(挂起)。

    要解决这个问题,您需要做两件事:

    示例:

        @EnableAutoConfiguration
        @EnableAsync
        public static class SpringApp extends SpringApplication {
    
            @Bean
            public TaskExecutor taskExecutor () {
                return new SimpleAsyncTaskExecutor();
            }
    
            @Autowired
            private Service service;
    
            @EventListener
            public void handleContextRefresh(ContextRefreshedEvent event){
                CompletableFuture<Void> aggregateFuture = service.doWork();
                // avoid exiting this method before all job complected prevents app from hanging
                aggregateFuture.join();
    
            }
        }
        public static void main(String[] args) {
            SpringApplicationBuilder app = new SpringApplicationBuilder(SpringApp.class).web(WebApplicationType.NONE);
            app.run()
               .close();  // <--- THIS!
        }