我创建了一个spring boot thread pool项目,它有一个需要在生成后24x7运行的线程,但是当我需要在服务器中停止应用程序进行一些维护时,它应该在完成当前任务后关闭,而不是接受任何新任务。
我的代码是:Config类
@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;
}
}
Runnable类
@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");
}
}
我需要帮助才能理解我是否需要停止我的spring启动应用程序它应该停止运行的所有20个线程运行(24x7),否则在完成while循环和退出时的当前循环之后。
我会在此代码中提出一些更改来解决问题
1)因为在你的POC processCommand调用Thread.sleep,当你关闭执行程序并且它中断了工作程序InterruptedException被调用但在你的代码中几乎被忽略。之后有if(Thread.currentThread()。isInterrupted())检查哪个将返回false,原因如上。类似的问题在下面的帖子中列出
how does thread.interrupt() sets the flag?
以下代码更改应该解决问题:
private void processCommand() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
shutdown();
}
}
2)还因为ThreadConfig :: taskExecutor executorPool.setWaitForTasksToCompleteOnShutdown(true)Spring将调用executor.shutdown而不是executor.shutdownNow。根据javadoc ExecutorService.shutdown
启动有序关闭,其中先前提交的任务将被执行,但不会接受任何新任务。
所以我建议设置
executorPool.setWaitForTasksToCompleteOnShutdown(false);
在这段代码中需要改进的其他事项:虽然DataMigration被注释为一个组件,但是这个类的实例不是由Spring创建的。您应该尝试使用类似于ThreadConfig :: taskExecutor的工厂方法,以使Spring启动DataMigration实例,例如将其他bean注入DataMigration实例。
为了在linux环境下运行jar文件时关闭执行程序,您可以添加执行器模块并启用关闭端点:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在application.properties中:
endpoints.shutdown.enabled=true
它将启用JMX关闭端点,您可以在其上调用shutdown。如果您想要完成任务的当前作业周期,您应该设置
executorPool.setWaitForTasksToCompleteOnShutdown(true);
为了远程连接到linux env上的jvm进程,你必须指定一个RMI注册表端口。这是一篇详细的文章:How to access Spring-boot JMX remotely
如果您只需要从本地环境连接到JMX,您可以运行jsoncole或命令行工具:Calling JMX MBean method from a shell script
以下是使用这些工具之一的示例 - 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...;
}