From a0319b1f91cf131d4f1f1801692898c727651fb7 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 (cherry picked from commit 20970a4a37e962cdec6cfbe58ead8a3ad1c56d54) --- .../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 b42fa2a2f63..7c94d019a67 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; @@ -1067,11 +1068,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(); } @@ -1084,6 +1083,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. @@ -1095,7 +1118,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(); @@ -1117,12 +1140,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();