Backend Development 19 min read

Graceful Shutdown of Spring Boot Applications: Avoiding kill -9 and Using Actuator, Custom Configurations, and PreDestroy

This article explains why using kill -9 to terminate Linux processes can cause data loss, demonstrates how to gracefully stop Spring Boot services with kill -15, the Actuator shutdown endpoint, and custom shutdown configurations, and shows how to add pre‑destroy hooks for backup tasks.

Architecture Digest
Architecture Digest
Architecture Digest
Graceful Shutdown of Spring Boot Applications: Avoiding kill -9 and Using Actuator, Custom Configurations, and PreDestroy

The kill command sends signals to processes; the default SIGTERM (15) requests termination, while SIGKILL (9) forcefully kills a process, which can lead to data inconsistency, especially with non‑transactional storage engines like MyISAM.

Using kill -9 on a running service is comparable to a sudden power outage, potentially leaving database updates half‑completed and causing irreversible errors in production environments.

To stop a Java application gracefully, Tomcat provides a shutdown script, and Spring Boot offers several approaches:

Step 1: Stop accepting new requests and internal threads.

Step 2: Check for running threads.

Step 3: Wait for active threads to finish.

Step 4: Stop the container.

One simple method is sending kill -15 <pid> , which triggers an interrupt on the sleeping thread but still allows the thread to finish its work before exiting.

@GetMapping(value = "/test")
public String test(){
    log.info("test --- start");
    try {
        Thread.sleep(100000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    log.info("test --- end");
    return "test";
}

Running the application with sudo mvn spring-boot:run starts the service on port 9988. After locating the process ID using sudo ps -ef | grep shutdown , the test endpoint can be called, and the process can be terminated with sudo kill -15 14086 . The logs show the interrupt handling and the final output.

2020-04-24 10:53:14.939  INFO 14086 --- [nio-9988-exec-1] com.ymy.controller.TestController : test --- start
2020-04-24 10:54:02.450  INFO 14086 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    ...
2020-04-24 10:54:04.574  INFO 14086 --- [nio-9988-exec-1] com.ymy.controller.TestController : test --- end

Spring Boot’s Actuator can expose a /shutdown endpoint when the dependency spring-boot-starter-actuator is added and the endpoint is enabled in application.yml :

management:
  endpoints:
    web:
      exposure:
        include: shutdown
  endpoint:
    shutdown:
      enabled: true

For more control, a custom graceful shutdown configuration can be implemented. The ElegantShutdownConfig class pauses the Tomcat connector, waits for the thread pool to finish, and logs a message if forced termination is required.

public class ElegantShutdownConfig implements TomcatConnectorCustomizer, ApplicationListener
{
    private volatile Connector connector;
    private final int waitTime = 10;
    @Override
    public void customize(Connector connector) { this.connector = connector; }
    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        connector.pause();
        Executor executor = connector.getProtocolHandler().getExecutor();
        if (executor instanceof ThreadPoolExecutor) {
            ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
            threadPoolExecutor.shutdown();
            if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
                System.out.println("请尝试暴力关闭");
            }
        }
    }
}

The configuration bean is registered in the main application class, and the customizer is added to the Tomcat factory.

@SpringBootApplication
public class ShutdownServerApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(ShutdownServerApplication.class, args);
        run.registerShutdownHook();
    }
    @Bean
    public ElegantShutdownConfig elegantShutdownConfig() { return new ElegantShutdownConfig(); }
    @Bean
    public ServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
        tomcat.addConnectorCustomizers(elegantShutdownConfig());
        return tomcat;
    }
}

To perform actions before the JVM shuts down, a @PreDestroy method can be added, for example, a data backup utility:

@Configuration
public class DataBackupConfig {
    @PreDestroy
    public void backData(){
        System.out.println("正在备份数据。。。。。。。。。。。");
    }
}

These techniques allow Spring Boot services to be stopped gracefully, ensuring ongoing requests complete, thread pools are properly terminated, and any necessary cleanup or backup tasks are executed before the application exits.

JavaProcess ManagementLinuxSpring BootGraceful ShutdownActuatorPreDestroy
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.