0

I have created spring boot thread pool project which has thread that needs to run 24x7 once spawned but when I need to stop the app in server for some maintenance it should shutdown after completing its current task and not taking up any new task.

My code for the same is: 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;
}
}

Runnable class

@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;
}

}

Main class:

@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");

}
}

I need help to understand if I need to stop my spring boot application it should stop all the 20 threads running which runs (24x7) otherwise after completing there current loop in while loop and exit.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Java_learner
  • 29
  • 1
  • 6

1 Answers1

1

I would propose couple of changes in this code to resolve the problem

1) since in your POC processCommand calls Thread.sleep, when you shutdown the executor and it interrupts workers InterruptedException get called but is almost ignored in your code. After that there is if(Thread.currentThread().isInterrupted()) check which will return false for the reason above. Similar problem is outlined in the post below

how does thread.interrupt() sets the flag?

the following code change should fix the problem:

private void processCommand() {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
        shutdown();
    }
}

2) Also because of ThreadConfig::taskExecutor executorPool.setWaitForTasksToCompleteOnShutdown(true) Spring will call executor.shutdown instead of executor.shutdownNow. According to javadoc ExecutorService.shutdown

Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted.

So I would recommend to set

executorPool.setWaitForTasksToCompleteOnShutdown(false);

Other things to improve in this code: although DataMigration is annotated as a component the instances of this class are creared not by Spring. You should try using factory method similar to ThreadConfig::taskExecutor in order to make Spring initiate instances of DataMigration for example to inject other bean into DataMigration instances.

In order to shutdown executor when running jar file on linux environment you can for example add actuator module and enable shutdown endpoint:

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

in application.properties:

endpoints.shutdown.enabled=true

It will enable JMX shutdown endpoint and you can call shutdown on it. If you want current job cycle of the task to be finished you should set

executorPool.setWaitForTasksToCompleteOnShutdown(true);

In order to connect to your jvm process on linux env remotely you have to specify an RMI Registry port. Here is a detailed article: How to access Spring-boot JMX remotely

If you just need to connect to JMX from local env you can run jsoncole or command-line tools : Calling JMX MBean method from a shell script

Here is an example uf using one of these tools - 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...;
}
Alex M981
  • 2,264
  • 14
  • 24
  • Thanks for the suggestion. But I need to understand one more thing.. one I create a jar of this spring boot and run in linux environment. How will I stop this process. Because I need to design it as running 24x7 and perform some task and when I try to stop the spring boot it should complete current executing task and stop before starting a new while loop. please help me with code design. Thanks – Java_learner Dec 27 '17 at 15:04
  • clarified the answer. hope it helps – Alex M981 Dec 28 '17 at 07:21
  • Thanks. For the reply! And how to call JMX shutdown endpoint in linux env for shutting down the application. – Java_learner Dec 28 '17 at 19:48
  • updated the answer with clarifications you asked for. – Alex M981 Dec 29 '17 at 08:46
  • thanks for the help. One more question if I want to inject class bean in DataMigration class so that class method and instance variables can be used by threads parallely to perform a task and the thread operation being thread safe at the same time. How can I inject class bean with autowire the methods are not thread safe. I guess being injected as singleton but I want parallel execution instead of sequential execution. – Java_learner Dec 30 '17 at 01:12