1
1
/*
2
- * Copyright 2002-2017 the original author or authors.
2
+ * Copyright 2002-2018 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
17
17
package org .springframework .scheduling .concurrent ;
18
18
19
19
import java .util .Date ;
20
+ import java .util .Map ;
20
21
import java .util .concurrent .Callable ;
21
22
import java .util .concurrent .Executor ;
22
23
import java .util .concurrent .ExecutorService ;
37
38
import org .springframework .scheduling .Trigger ;
38
39
import org .springframework .scheduling .support .TaskUtils ;
39
40
import org .springframework .util .Assert ;
41
+ import org .springframework .util .ConcurrentReferenceHashMap ;
40
42
import org .springframework .util .ErrorHandler ;
41
43
import org .springframework .util .concurrent .ListenableFuture ;
42
44
import org .springframework .util .concurrent .ListenableFutureTask ;
@@ -67,6 +69,10 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
67
69
@ Nullable
68
70
private ScheduledExecutorService scheduledExecutor ;
69
71
72
+ // Underlying ScheduledFutureTask to user-level ListenableFuture handle, if any
73
+ private final Map <Object , ListenableFuture <?>> listenableFutureMap =
74
+ new ConcurrentReferenceHashMap <>(16 , ConcurrentReferenceHashMap .ReferenceType .WEAK );
75
+
70
76
71
77
/**
72
78
* Set the ScheduledExecutorService's pool size.
@@ -253,9 +259,9 @@ public <T> Future<T> submit(Callable<T> task) {
253
259
public ListenableFuture <?> submitListenable (Runnable task ) {
254
260
ExecutorService executor = getScheduledExecutor ();
255
261
try {
256
- ListenableFutureTask <Object > future = new ListenableFutureTask <>(task , null );
257
- executor . execute ( errorHandlingTask ( future , false ) );
258
- return future ;
262
+ ListenableFutureTask <Object > listenableFuture = new ListenableFutureTask <>(task , null );
263
+ executeAndTrack ( executor , listenableFuture );
264
+ return listenableFuture ;
259
265
}
260
266
catch (RejectedExecutionException ex ) {
261
267
throw new TaskRejectedException ("Executor [" + executor + "] did not accept task: " + task , ex );
@@ -266,15 +272,32 @@ public ListenableFuture<?> submitListenable(Runnable task) {
266
272
public <T > ListenableFuture <T > submitListenable (Callable <T > task ) {
267
273
ExecutorService executor = getScheduledExecutor ();
268
274
try {
269
- ListenableFutureTask <T > future = new ListenableFutureTask <>(task );
270
- executor . execute ( errorHandlingTask ( future , false ) );
271
- return future ;
275
+ ListenableFutureTask <T > listenableFuture = new ListenableFutureTask <>(task );
276
+ executeAndTrack ( executor , listenableFuture );
277
+ return listenableFuture ;
272
278
}
273
279
catch (RejectedExecutionException ex ) {
274
280
throw new TaskRejectedException ("Executor [" + executor + "] did not accept task: " + task , ex );
275
281
}
276
282
}
277
283
284
+ private void executeAndTrack (ExecutorService executor , ListenableFutureTask <?> listenableFuture ) {
285
+ Future <?> scheduledFuture = executor .submit (errorHandlingTask (listenableFuture , false ));
286
+ this .listenableFutureMap .put (scheduledFuture , listenableFuture );
287
+ listenableFuture .addCallback (result -> listenableFutureMap .remove (scheduledFuture ),
288
+ ex -> listenableFutureMap .remove (scheduledFuture ));
289
+ }
290
+
291
+ @ Override
292
+ protected void cancelRemainingTask (Runnable task ) {
293
+ super .cancelRemainingTask (task );
294
+ // Cancel associated user-level ListenableFuture handle as well
295
+ ListenableFuture <?> listenableFuture = this .listenableFutureMap .get (task );
296
+ if (listenableFuture != null ) {
297
+ listenableFuture .cancel (true );
298
+ }
299
+ }
300
+
278
301
@ Override
279
302
public boolean prefersShortLivedTasks () {
280
303
return true ;
0 commit comments