@@ -87,6 +87,8 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator
87
87
88
88
private @ Nullable Set <Thread > activeThreads ;
89
89
90
+ private boolean cancelRemainingTasksOnClose = false ;
91
+
90
92
private boolean rejectTasksWhenLimitReached = false ;
91
93
92
94
private volatile boolean active = true ;
@@ -178,12 +180,33 @@ public void setTaskDecorator(TaskDecorator taskDecorator) {
178
180
* @param timeout the timeout in milliseconds
179
181
* @since 6.1
180
182
* @see #close()
183
+ * @see #setCancelRemainingTasksOnClose
181
184
* @see org.springframework.scheduling.concurrent.ExecutorConfigurationSupport#setAwaitTerminationMillis
182
185
*/
183
186
public void setTaskTerminationTimeout (long timeout ) {
184
187
Assert .isTrue (timeout >= 0 , "Timeout value must be >=0" );
185
188
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 ();
187
210
}
188
211
189
212
/**
@@ -243,6 +266,15 @@ public boolean isActive() {
243
266
return this .active ;
244
267
}
245
268
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
+
246
278
247
279
/**
248
280
* Executes the given task, within a concurrency throttle
@@ -331,7 +363,7 @@ protected Thread newThread(Runnable task) {
331
363
}
332
364
333
365
/**
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
335
367
* {@link #setTaskTerminationTimeout task termination timeout} has been set.
336
368
* Otherwise, it is not necessary to close this executor.
337
369
* @since 6.1
@@ -342,17 +374,26 @@ public void close() {
342
374
this .active = false ;
343
375
Set <Thread > threads = this .activeThreads ;
344
376
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 ();
349
390
}
350
391
}
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 );
353
395
}
354
396
}
355
- threads .forEach (Thread ::interrupt );
356
397
}
357
398
}
358
399
}
0 commit comments