我已经创建了Spring Boot线程池项目,该项目的线程一旦产生就需要运行24x7,但是当我需要在服务器中停止应用程序以进行某种维护时,它应该在完成当前任务后关闭并且不承担任何新任务。
我的代码是:config class
@Configuration
public class ThreadConfig {
@Bean
public ThreadPoolTaskExecutor taskExecutor(){
ThreadPoolTaskExecutor executorPool = new ThreadPoolTaskExecutor();
executorPool.setCorePoolSize(10);
executorPool.setMaxPoolSize(20);
executorPool.setQueueCapacity(10);
executorPool.setWaitForTasksToCompleteOnShutdown(true);
executorPool.setAwaitTerminationSeconds(60);
executorPool.initialize();
return executorPool;
}
}
可运行的类
@Component
@Scope("prototype")
public class DataMigration implements Runnable {
String name;
private boolean run=true;
public DataMigration(String name) {
this.name = name;
}
@Override
public void run() {
while(run){
System.out.println(Thread.currentThread().getName()+" Start Thread = "+name);
processCommand();
System.out.println(Thread.currentThread().getName()+" End Thread = "+name);
if(Thread.currentThread().isInterrupted()){
System.out.println("Thread Is Interrupted");
break;
}
}
}
private void processCommand() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void shutdown(){
this.run = false;
}
}
主类:
@SpringBootApplication
public class DataMigrationPocApplication implements CommandLineRunner{
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
public static void main(String[] args) {
SpringApplication.run(DataMigrationPocApplication.class, args);
}
@Override
public void run(String... arg0) throws Exception {
for(int i = 1; i<=20 ; i++){
taskExecutor.execute(new DataMigration("Task " + i));
}
for (;;) {
int count = taskExecutor.getActiveCount();
System.out.println("Active Threads : " + count);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 0) {
taskExecutor.shutdown();
break;
}
}
System.out.println("Finished all threads");
}
}
我需要帮助来了解我是否需要停止春季启动应用程序,它应该停止运行的所有20个线程(24x7),否则在循环和退出时完成当前循环后。
我会在此代码中提出几个更改以解决问题
1)由于在您的POC ProcessCommand呼叫thread.sleep中,当您关闭执行程序时,它会中断工人中断的呼叫,但在您的代码中几乎被忽略。之后,有 if(thread.currentthread()。is Interrupted())检查该检查将返回false,以上述原因。下面的帖子概述了类似的问题
thread.interrupt()如何设置标志?
以下代码更改应解决问题:
private void processCommand() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
shutdown();
}
}
2)还因为threadConfig :: Taskexecutor executorpool.setWaitfortaskStocompleteonShutdown(true)Spring将调用executor.shutdown而不是exector.shutdownnow。根据javadoc executorService.shutdown
启动有序的关闭,以前提交的任务是 执行,但不会接受新任务。
所以我建议设置
executorPool.setWaitForTasksToCompleteOnShutdown(false);
在此代码中需要改进的其他事情:尽管数据登录作为组成部分,但该类的实例并非在春季之前将其编入。您应该尝试使用类似于ThreadConfig :: Taskexecutor类似的工厂方法,以使春季启动Datamigrations例如,将其他Bean注入Datamigration实例。
为了关闭执行程序,当在Linux环境上运行JAR文件您可以添加执行器模块并启用关闭端点:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在application.properties:
endpoints.shutdown.enabled=true
它将启用JMX关闭端点,您可以在其上调用关闭。如果您希望完成任务的当前工作周期,则应设置
executorPool.setWaitForTasksToCompleteOnShutdown(true);
为了在Linux Env 上连接到JVM进程>远程指定RMI注册表端口。这是一篇详细的文章:如何访问Spring-boot JMX远程
如果您只需要从本地env连接到JMX,则可以运行JSONCOLE或命令行工具:从Shell脚本调用JMX MBEAN方法
这是使用以下工具之一的示例uf -jmxterm
$>run -d org.springframework.boot: -b org.springframework.boot:name=shutdownEndpoint,type=Endpoint shutdown
#calling operation shutdown of mbean org.springframework.boot:name=shutdownEndpoint,type=Endpoint with params []
#operation returns:
{
message = Shutting down, bye...;
}