From 20970a4a37e962cdec6cfbe58ead8a3ad1c56d54 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 6 Feb 2026 19:03:54 +0100 Subject: [PATCH] Repeatedly check status while trying to lock for shutdown Closes gh-36260 --- .../support/AbstractApplicationContext.java | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java index 63b6f67f853..dc3da9309eb 100644 --- a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java @@ -27,6 +27,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -1070,11 +1071,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) { @Override public void run() { - if (isStartupShutdownThreadStuck()) { - active.set(false); + if (!tryLockForShutdown()) { return; } - startupShutdownLock.lock(); try { doClose(); } @@ -1087,6 +1086,30 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader } } + /** + * Try to acquire the common startup/shutdown lock, backing out if + * the main startup/shutdown thread is stuck or on interruption. + * @see #isStartupShutdownThreadStuck() + */ + private boolean tryLockForShutdown() { + try { + while (!this.startupShutdownLock.tryLock(100, TimeUnit.MILLISECONDS)) { + if (!this.active.get() || this.closed.get()) { + return false; + } + if (isStartupShutdownThreadStuck()) { + this.active.set(false); + return false; + } + } + return true; + } + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + return false; + } + } + /** * Determine whether an active startup/shutdown thread is currently stuck, * for example, through a {@code System.exit} call in a user component. @@ -1098,7 +1121,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader activeThread.interrupt(); try { // Leave just a little bit of time for the interruption to show effect - Thread.sleep(1); + Thread.sleep(10); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); @@ -1120,12 +1143,10 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader */ @Override public void close() { - if (isStartupShutdownThreadStuck()) { - this.active.set(false); + if (!tryLockForShutdown()) { return; } - this.startupShutdownLock.lock(); try { this.startupShutdownThread = Thread.currentThread();