@@ -92,6 +92,8 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator
92
92
@ Nullable
93
93
private Set <Thread > activeThreads ;
94
94
95
+ private boolean cancelRemainingTasksOnClose = false ;
96
+
95
97
private boolean rejectTasksWhenLimitReached = false ;
96
98
97
99
private volatile boolean active = true ;
@@ -184,12 +186,33 @@ public void setTaskDecorator(TaskDecorator taskDecorator) {
184
186
* @param timeout the timeout in milliseconds
185
187
* @since 6.1
186
188
* @see #close()
189
+ * @see #setCancelRemainingTasksOnClose
187
190
* @see org.springframework.scheduling.concurrent.ExecutorConfigurationSupport#setAwaitTerminationMillis
188
191
*/
189
192
public void setTaskTerminationTimeout (long timeout ) {
190
193
Assert .isTrue (timeout >= 0 , "Timeout value must be >=0" );
191
194
this .taskTerminationTimeout = timeout ;
192
- this .activeThreads = (timeout > 0 ? ConcurrentHashMap .newKeySet () : null );
195
+ trackActiveThreadsIfNecessary ();
196
+ }
197
+
198
+ /**
199
+ * Specify whether to cancel remaining tasks on close: that is, whether to
200
+ * interrupt any active threads at the time of the {@link #close()} call.
201
+ * <p>The default is {@code false}, not tracking active threads at all or
202
+ * just interrupting any remaining threads that still have not finished after
203
+ * the specified {@link #setTaskTerminationTimeout taskTerminationTimeout}.
204
+ * Switch this to {@code true} for immediate interruption on close, either in
205
+ * combination with a subsequent termination timeout or without any waiting
206
+ * at all, depending on whether a {@code taskTerminationTimeout} has been
207
+ * specified as well.
208
+ * @since 6.2.11
209
+ * @see #close()
210
+ * @see #setTaskTerminationTimeout
211
+ * @see org.springframework.scheduling.concurrent.ExecutorConfigurationSupport#setWaitForTasksToCompleteOnShutdown
212
+ */
213
+ public void setCancelRemainingTasksOnClose (boolean cancelRemainingTasksOnClose ) {
214
+ this .cancelRemainingTasksOnClose = cancelRemainingTasksOnClose ;
215
+ trackActiveThreadsIfNecessary ();
193
216
}
194
217
195
218
/**
@@ -249,6 +272,15 @@ public boolean isActive() {
249
272
return this .active ;
250
273
}
251
274
275
+ /**
276
+ * Track active threads only when a task termination timeout has been
277
+ * specified or interruption of remaining threads has been requested.
278
+ */
279
+ private void trackActiveThreadsIfNecessary () {
280
+ this .activeThreads = (this .taskTerminationTimeout > 0 || this .cancelRemainingTasksOnClose ?
281
+ ConcurrentHashMap .newKeySet () : null );
282
+ }
283
+
252
284
253
285
/**
254
286
* Executes the given task, within a concurrency throttle
@@ -353,7 +385,7 @@ protected Thread newThread(Runnable task) {
353
385
}
354
386
355
387
/**
356
- * This close methods tracks the termination of active threads if a concrete
388
+ * This close method tracks the termination of active threads if a concrete
357
389
* {@link #setTaskTerminationTimeout task termination timeout} has been set.
358
390
* Otherwise, it is not necessary to close this executor.
359
391
* @since 6.1
@@ -364,17 +396,26 @@ public void close() {
364
396
this .active = false ;
365
397
Set <Thread > threads = this .activeThreads ;
366
398
if (threads != null ) {
367
- synchronized (threads ) {
368
- try {
369
- if (!threads .isEmpty ()) {
370
- threads .wait (this .taskTerminationTimeout );
399
+ if (this .cancelRemainingTasksOnClose ) {
400
+ // Early interrupt for remaining tasks on close
401
+ threads .forEach (Thread ::interrupt );
402
+ }
403
+ if (this .taskTerminationTimeout > 0 ) {
404
+ synchronized (threads ) {
405
+ try {
406
+ if (!threads .isEmpty ()) {
407
+ threads .wait (this .taskTerminationTimeout );
408
+ }
409
+ }
410
+ catch (InterruptedException ex ) {
411
+ Thread .currentThread ().interrupt ();
371
412
}
372
413
}
373
- catch (InterruptedException ex ) {
374
- Thread .currentThread ().interrupt ();
414
+ if (!this .cancelRemainingTasksOnClose ) {
415
+ // Late interrupt for remaining tasks after timeout
416
+ threads .forEach (Thread ::interrupt );
375
417
}
376
418
}
377
- threads .forEach (Thread ::interrupt );
378
419
}
379
420
}
380
421
}
0 commit comments