@@ -87,6 +87,8 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator
8787
8888	private  @ Nullable  Set <Thread > activeThreads ;
8989
90+ 	private  boolean  cancelRemainingTasksOnClose  = false ;
91+ 
9092	private  boolean  rejectTasksWhenLimitReached  = false ;
9193
9294	private  volatile  boolean  active  = true ;
@@ -178,12 +180,33 @@ public void setTaskDecorator(TaskDecorator taskDecorator) {
178180	 * @param timeout the timeout in milliseconds 
179181	 * @since 6.1 
180182	 * @see #close() 
183+ 	 * @see #setCancelRemainingTasksOnClose 
181184	 * @see org.springframework.scheduling.concurrent.ExecutorConfigurationSupport#setAwaitTerminationMillis 
182185	 */ 
183186	public  void  setTaskTerminationTimeout (long  timeout ) {
184187		Assert .isTrue (timeout  >= 0 , "Timeout value must be >=0" );
185188		this .taskTerminationTimeout  = timeout ;
186- 		this .activeThreads  = (timeout  > 0  ? ConcurrentHashMap .newKeySet () : null );
189+ 		trackActiveThreadsIfNecessary ();
190+ 	}
191+ 
192+ 	/** 
193+ 	 * Specify whether to cancel remaining tasks on close: that is, whether to 
194+ 	 * interrupt any active threads at the time of the {@link #close()} call. 
195+ 	 * <p>The default is {@code false}, not tracking active threads at all or 
196+ 	 * just interrupting any remaining threads that still have not finished after 
197+ 	 * the specified {@link #setTaskTerminationTimeout taskTerminationTimeout}. 
198+ 	 * Switch this to {@code true} for immediate interruption on close, either in 
199+ 	 * combination with a subsequent termination timeout or without any waiting 
200+ 	 * at all, depending on whether a {@code taskTerminationTimeout} has been 
201+ 	 * specified as well. 
202+ 	 * @since 6.2.11 
203+ 	 * @see #close() 
204+ 	 * @see #setTaskTerminationTimeout 
205+ 	 * @see org.springframework.scheduling.concurrent.ExecutorConfigurationSupport#setWaitForTasksToCompleteOnShutdown 
206+ 	 */ 
207+ 	public  void  setCancelRemainingTasksOnClose (boolean  cancelRemainingTasksOnClose ) {
208+ 		this .cancelRemainingTasksOnClose  = cancelRemainingTasksOnClose ;
209+ 		trackActiveThreadsIfNecessary ();
187210	}
188211
189212	/** 
@@ -243,6 +266,15 @@ public boolean isActive() {
243266		return  this .active ;
244267	}
245268
269+ 	/** 
270+ 	 * Track active threads only when a task termination timeout has been 
271+ 	 * specified or interruption of remaining threads has been requested. 
272+ 	 */ 
273+ 	private  void  trackActiveThreadsIfNecessary () {
274+ 		this .activeThreads  = (this .taskTerminationTimeout  > 0  || this .cancelRemainingTasksOnClose  ?
275+ 				ConcurrentHashMap .newKeySet () : null );
276+ 	}
277+ 
246278
247279	/** 
248280	 * Executes the given task, within a concurrency throttle 
@@ -331,7 +363,7 @@ protected Thread newThread(Runnable task) {
331363	}
332364
333365	/** 
334- 	 * This close methods  tracks the termination of active threads if a concrete 
366+ 	 * This close method  tracks the termination of active threads if a concrete 
335367	 * {@link #setTaskTerminationTimeout task termination timeout} has been set. 
336368	 * Otherwise, it is not necessary to close this executor. 
337369	 * @since 6.1 
@@ -342,17 +374,26 @@ public void close() {
342374			this .active  = false ;
343375			Set <Thread > threads  = this .activeThreads ;
344376			if  (threads  != null ) {
345- 				synchronized  (threads ) {
346- 					try  {
347- 						if  (!threads .isEmpty ()) {
348- 							threads .wait (this .taskTerminationTimeout );
377+ 				if  (this .cancelRemainingTasksOnClose ) {
378+ 					// Early interrupt for remaining tasks on close 
379+ 					threads .forEach (Thread ::interrupt );
380+ 				}
381+ 				if  (this .taskTerminationTimeout  > 0 ) {
382+ 					synchronized  (threads ) {
383+ 						try  {
384+ 							if  (!threads .isEmpty ()) {
385+ 								threads .wait (this .taskTerminationTimeout );
386+ 							}
387+ 						}
388+ 						catch  (InterruptedException  ex ) {
389+ 							Thread .currentThread ().interrupt ();
349390						}
350391					}
351- 					catch  (InterruptedException  ex ) {
352- 						Thread .currentThread ().interrupt ();
392+ 					if  (!this .cancelRemainingTasksOnClose ) {
393+ 						// Late interrupt for remaining tasks after timeout 
394+ 						threads .forEach (Thread ::interrupt );
353395					}
354396				}
355- 				threads .forEach (Thread ::interrupt );
356397			}
357398		}
358399	}
0 commit comments