我有一个Spring引导应用程序,它使用CommandLineRunner
和Spring @Async
注释来异步运行方法。一切正常,但是当我的所有线程都完成后,应用程序就会挂起而不是退出。
以下是我在应用程序中的最小示例:
Application.java :
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
ApplicationStartup.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();
}
}
}
AsyncService.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个线程。
关于为什么应用程序没有退出上述配置的任何想法?
答案 0 :(得分:1)
您错过了加入异步作业,这就是run
方法在所有线程完成之前退出(远)的原因 - 并且尴尬的行为是&#34;更多可理解&#34;
根据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);
答案 1 :(得分:0)
即使marked as correct答案是有效的。这不是完整的答案。
在没有@EnableAsync
且没有WEB环境.web(WebApplicationType.NONE)
的情况下,spring boot应用程序一旦启动就自动停止(因为无事可做/等待)。因此,即使您不在应用中执行apringApp.close()
,而仅在app.run(commandLine)
中执行,.close()
方法也会自动调用。
但是一旦您添加了@EnableAsync
-行为就会发生变化,因为可能会有异步工作,因此应用程序一旦启动就不会停止。而且,如果没有停止代码,则该应用程序仍然可以运行(挂起)。
要解决此问题,您需要做两件事:
.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!
}
答案 2 :(得分:0)
是的,我遇到了与CommandLine应用类似的观察。在完成所有异步任务后,该应用程序不会关闭。按照@msangel的建议,我必须使用app.run()。close()关闭该应用程序。