Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 异步方法调用完成后,Spring命令行应用程序挂起_Java_Spring_Multithreading_Asynchronous_Spring Boot - Fatal编程技术网

Java 异步方法调用完成后,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

我有一个Spring引导应用程序,它使用
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()来解决这个问题;