Graceful Shutdown of Spring Boot Applications: Risks of kill -9 and Proper Termination Techniques
This article explains why using the forceful kill -9 command to stop Linux processes can cause data loss and inconsistent states, especially in distributed systems, and demonstrates several graceful shutdown methods for Spring Boot—including SIGTERM, ConfigurableApplicationContext.close(), Actuator shutdown endpoints, custom Tomcat connector handling, and @PreDestroy data‑backup hooks—complete with code examples and execution logs.
kill -9 pid ???
The kill command sends a signal to a process; by default it sends SIGTERM (15) to terminate the program, and if that fails you can use SIGKILL (9) to force termination. The process ID can be obtained with ps or jobs.
In simple terms, kill -9 pid forcefully kills a Linux process, which can be dangerous in production environments.
Problems caused by kill -9 pid
Because kill -9 is a violent termination, it can lead to serious consequences such as data inconsistency. For example, if a money‑transfer operation is interrupted, InnoDB would roll back safely, but MyISAM would leave one account debited and the other unchanged, effectively simulating a power outage.
In distributed systems, using kill -9 to stop a service bypasses transaction guarantees and may cause unrecoverable data loss.
Even with MyISAM, partial updates (e.g., updating only one of two related tables) can cause inconsistent user information, which is unacceptable for critical data.
Therefore, you should avoid using kill -9 to stop services and instead use graceful shutdown mechanisms.
Graceful termination of services
Step 1: Stop accepting new requests and internal threads. Step 2: Check if any threads are still running. Step 3: Wait for running threads to finish. Step 4: Stop the container.
Below are several graceful shutdown solutions for Spring Boot.
Graceful shutdown
kill -15 pid
Sending SIGTERM (15) allows the JVM to interrupt sleeping threads and shut down cleanly.
@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";
}
1234567891011When the process is killed with kill -15 14086, the thread receives an interrupt, the sleep throws InterruptedException, and the method finishes, printing "test --- end".
application.yml
server:
port: 9988
12Start the project
sudo mvn spring-boot:run
1Find the process ID:
sudo ps -ef | grep shutdown
1Test the endpoint and then stop the process:
sudo curl 127.0.0.1:9988/test
1 sudo kill -15 14086
1Log output shows the interrupt and the method completing, confirming a graceful shutdown.
ConfigurableApplicationContext close
Implement a controller that injects the application context and calls close() on it.
package com.ymy.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class TestController implements ApplicationContextAware {
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
@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";
}
/**
* Shutdown
*/
@PostMapping(value = "shutdown")
public void shutdown() {
ConfigurableApplicationContext cyx = (ConfigurableApplicationContext) context;
cyx.close();
}
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344The call to cyx.close() triggers the JVM shutdown hook, allowing Spring Boot to stop gracefully.
Actuator shutdown endpoint
Add the Actuator dependency and enable the shutdown endpoint in application.yml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
12345 server:
port: 9988
management:
endpoints:
web:
exposure:
include: shutdown
endpoint:
shutdown:
enabled: true
server:
port: 8888
12345678910111213Expose a custom shutdown URL and invoke it while a request is in progress. The response confirms the shutdown, and the logs show the interrupt handling similar to the previous methods.
Custom Tomcat graceful shutdown (ElegantShutdownConfig)
Define a Tomcat connector customizer that pauses the connector and waits for the thread pool to finish:
package com.ymy.config;
import org.apache.catalina.connector.Connector;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ElegantShutdownConfig implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
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) {
try {
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
threadPoolExecutor.shutdown();
if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
System.out.println("请尝试暴力关闭");
}
} catch (InterruptedException ex) {
System.out.println("异常了");
Thread.currentThread().interrupt();
}
}
}
}
1234567891011121314151617181920212223242526272829303132333435363738394041Register this bean in the main application class and add it to the Tomcat factory:
package com.ymy;
import com.ymy.config.ElegantShutdownConfig;
import org.apache.catalina.connector.Connector;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
@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;
}
}
1234567891011121314151617181920212223242526272829303132333435363738394041When the /test endpoint is sleeping, the server now waits the configured waitTime (10 seconds) before terminating, avoiding abrupt thread interruption.
Data backup during shutdown
You can execute backup logic before the container stops by adding a method annotated with @PreDestroy:
package com.ymy.config;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PreDestroy;
@Configuration
public class DataBackupConfig {
@PreDestroy
public void backData() {
System.out.println("正在备份数据。。。。。。。。。。。");
}
}
123456789101112131415After enabling this configuration, the console prints the backup message when the application shuts down.
PS: If you find this sharing useful, feel free to like and follow.
END
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
