diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java index b542dc464962..ba60d32a9874 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java @@ -32,6 +32,7 @@ import java.util.Properties; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; import org.apache.commons.logging.Log; @@ -67,7 +68,9 @@ import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; import org.springframework.context.annotation.ConfigurationClassPostProcessor; import org.springframework.context.aot.AotApplicationContextInitializer; +import org.springframework.context.event.ApplicationContextEvent; import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.GenericTypeResolver; @@ -417,9 +420,7 @@ private void prepareContext(DefaultBootstrapContext bootstrapContext, Configurab context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } if (this.keepAlive) { - KeepAlive keepAlive = new KeepAlive(); - keepAlive.start(); - context.addApplicationListener(keepAlive); + context.addApplicationListener(new KeepAlive()); } context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context)); if (!AotDetector.useGeneratedArtifacts()) { @@ -1635,31 +1636,47 @@ public SpringApplicationRunListener getRunListener(SpringApplication springAppli } /** - * A non-daemon thread to keep the JVM alive. Reacts to {@link ContextClosedEvent} to - * stop itself when the application context is closed. + * Starts a non-daemon thread to keep the JVM alive on {@link ContextRefreshedEvent}. + * Stops the thread on {@link ContextClosedEvent}. */ - private static final class KeepAlive extends Thread implements ApplicationListener { + private static final class KeepAlive implements ApplicationListener { - KeepAlive() { - setName("keep-alive"); - setDaemon(false); - } + private final AtomicReference thread = new AtomicReference<>(); @Override - public void onApplicationEvent(ContextClosedEvent event) { - interrupt(); + public void onApplicationEvent(ApplicationContextEvent event) { + if (event instanceof ContextRefreshedEvent) { + startKeepAliveThread(); + } + else if (event instanceof ContextClosedEvent) { + stopKeepAliveThread(); + } } - @Override - public void run() { - while (true) { - try { - Thread.sleep(Long.MAX_VALUE); - } - catch (InterruptedException ex) { - break; + private void startKeepAliveThread() { + Thread thread = new Thread(() -> { + while (true) { + try { + Thread.sleep(Long.MAX_VALUE); + } + catch (InterruptedException ex) { + break; + } } + }); + if (this.thread.compareAndSet(null, thread)) { + thread.setDaemon(false); + thread.setName("keep-alive"); + thread.start(); + } + } + + private void stopKeepAliveThread() { + Thread thread = this.thread.getAndSet(null); + if (thread == null) { + return; } + thread.interrupt(); } }