From cff48fff2d681a97443a2f228e2e34a5900545c9 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 24 Feb 2026 17:17:21 +0100 Subject: [PATCH] Reject late-executing tasks after termination waiting Closes gh-36362 --- .../core/task/SimpleAsyncTaskExecutor.java | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java b/spring-core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java index f8d44c68225..6440e28ceb7 100644 --- a/spring-core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java +++ b/spring-core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java @@ -96,6 +96,8 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator private volatile boolean active = true; + private volatile boolean cancelled = false; + /** * Create a new SimpleAsyncTaskExecutor with default thread name prefix. @@ -389,6 +391,7 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator Set threads = this.activeThreads; if (threads != null) { if (this.cancelRemainingTasksOnClose) { + this.cancelled = true; // Early interrupt for remaining tasks on close threads.forEach(Thread::interrupt); } @@ -402,6 +405,7 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator catch (InterruptedException ex) { Thread.currentThread().interrupt(); } + this.cancelled = true; } if (!this.cancelRemainingTasksOnClose) { // Late interrupt for remaining tasks after timeout @@ -412,6 +416,12 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator } } + private void checkCancelled() { + if (this.cancelled) { + throw new TaskRejectedException(getClass().getSimpleName() + " has cancelled all remaining tasks"); + } + } + /** * Subclass of the general ConcurrencyThrottleSupport class, @@ -464,16 +474,27 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator Thread thread = null; if (threads != null) { thread = Thread.currentThread(); - threads.add(thread); + if (isActive()) { + threads.add(thread); + } + else { + synchronized (threads) { + checkCancelled(); + threads.add(thread); + } + } } try { this.task.run(); } finally { if (threads != null) { - threads.remove(thread); - if (!isActive()) { + if (isActive()) { + threads.remove(thread); + } + else { synchronized (threads) { + threads.remove(thread); if (threads.isEmpty()) { threads.notify(); }