Java 异步方法调用完成后,Spring命令行应用程序挂起
我有一个Spring引导应用程序,它使用Java 异步方法调用完成后,Spring命令行应用程序挂起,java,spring,multithreading,asynchronous,spring-boot,Java,Spring,Multithreading,Asynchronous,Spring Boot,我有一个Spring引导应用程序,它使用CommandLineRunner和Spring@Async注释异步运行一个方法。这一切都很好,但当我所有的线程都完成时,应用程序只是挂起而不是退出 以下是我的应用程序中的一个最小示例: Application.java: @SpringBootApplication @EnableAsync public class Application { public static void main(String[] args) { Sp
CommandLineRunner
和Spring@Async
注释异步运行一个方法。这一切都很好,但当我所有的线程都完成时,应用程序只是挂起而不是退出
以下是我的应用程序中的一个最小示例:
Application.java:
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@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();
}
}
}
@Service
@Transactional
public class AsyncService {
@Async
public void runAsyncMethod() {
//perform call to an API and process results
}
}
@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;
}
}
应用程序启动.java:
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@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();
}
}
}
@Service
@Transactional
public class AsyncService {
@Async
public void runAsyncMethod() {
//perform call to an API and process results
}
}
@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;
}
}
ExecutorConfig.java:
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@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();
}
}
}
@Service
@Transactional
public class AsyncService {
@Async
public void runAsyncMethod() {
//perform call to an API and process results
}
}
@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个线程
关于为什么应用程序没有按照上面的配置退出,你有什么想法吗?你没有加入异步作业,这就是为什么run
方法在所有线程完成之前退出(far)的原因,而尴尬的行为“更容易理解”
根据,您可以像这样加入:
...
CompletableFuture<Void>[] myJobs = new CompletableFuture<>[N];
...
for (int i = 0; i < N; i++) {
myJobs[i] = asyncService.runAsyncMethod();
}
...
CompletableFuture.allOf(myJobs).join();
。。。
CompletableFuture[]myJobs=新的CompletableFuture[N];
...
对于(int i=0;i
您的runAsyncMethod()
需要返回一个CompletableFuture
。为此,您可以返回CompletableFuture.completedFuture(null)编码>即使答案有效。这不是完整的答案
没有@EnableAsync
和没有WEB环境.WEB(WebApplicationType.NONE)
spring boot应用程序启动后会自动停止(因为无需执行任何操作/等待)。因此,即使您在应用程序中不执行apringApp.close()
,而只执行app.run(commandLine)
,也会自动调用.close()
方法
但一旦您添加了@EnableAsync
,行为就会改变,因为可能会有异步工作,所以应用程序在启动后不会停止。如果没有停止代码,应用程序将保持工作状态(挂起)
要解决此问题,您需要做两件事:
- 应用程序启动后隐式调用
.close()
样本:
@EnableAutoConfiguration
@使能同步
公共静态类SpringApp扩展了SpringApplication{
@豆子
公共任务执行器任务执行器(){
返回新的SimpleAsyncTaskExecutor();
}
@自动连线
私人服务;
@事件监听器
公共无效handleContextRefresh(上下文刷新事件){
CompletableFuture aggregateFuture=service.doWork();
//避免在所有作业完成之前退出此方法以防止应用程序挂起
aggregateFuture.join();
}
}
公共静态void main(字符串[]args){
SpringApplicationBuilder应用程序=新的SpringApplicationBuilder(SpringApp.class).web(WebApplicationType.NONE);
app.run()
.close();//是的,我使用命令行应用程序进行了类似的观察。在所有异步任务完成后,该应用程序不会关闭。正如@msangel所建议的,我必须使用app.run().close()关闭应用程序。事实上,我怀疑cmdrunner#run在异步方法完成之前退出…考虑在run方法中加入线程!(示例:)必须执行myCollection.toArray(新的CompletableFuture[myCollection.size()])
。但是在执行join()之后
它返回到我的主线程并执行logger.debug()
语句,但仍然挂在主线程中…哦,没关系,我只需等待60秒,最后一个线程就会关闭,因为我的keepAliveSeconds
设置为60@xerx593这非常有效!如果您想将其作为答案键入,包括您引用的示例的一些片段,我将接受它。OP问为什么他的应用程序挂起,而不是为什么它早就存在我不知道!(为什么挂起/提前退出)OP很高兴并“命令”了这个答案(接受它),你的问题是什么,@hairofdog?在添加ThreadPoolTaskExecutor
以使我的异步方法运行后…好吧,public static void main()
方法开始挂起。(与OP类似,但我使用的是注释ConfigApplicationContext
而不是SpringApplication
来初始化Spring容器。)我希望了解magic Spring的功能以及退出的正确方法。请发布一个新问题(包括配置详细信息),@HairofDog(并向我指出它..我也会尽力帮助你解决它;)…在这种情况下,这个问题可以通过“加入作业”来解决。谢谢,我可以在同一秒钟内通过使用:List results=new ArrayList(N);CompletableFuture.allOf(CompletableFuture.allOf(results.toArray(new CompletableFuture[0])).join()来解决这个问题;