I have a method (included below) to return the values of a list of CompletableFutures.
The method is supposed to:
be able to timeout after a given time.
be able to cancel all futures if there are more than n amount of exceptions.
The first point works well and indeed bombs out after it passed the timeout limit. (I still need to call exectuorService.shutdownNow() afterwards to return to the caller). The problem I'm having is with the second thing I'm trying to accomplish.
Lets say i have a list of 20,000 futures and all of them will have an exception, then why let all of them execute, if I see that there are too many exceptions then i assume that something is wrong with all of the futures andI want to cancel them.
In addition i would love to have a timeout on each future individually how long it may take, but this also would'nt work, unassuming for the same reason outlined below.
It seems that the reason is, because when I call allDoneFuture.thenApply(), at this point it waits and lets all the futures complete, either successfully or exceptionally. Only after all of them completed does it go through each future and fetches its result. At that point what good does it do to cancel, when they have completed already.
I would much appreciate if someone can show me how to accomplish this specific need: "Monitor the exceptions, and the individual timeouts, and based on that cancel all others".
Thanks.
Below is the method I wrote:
/**
* #param futures a list of completable futures
* #param timeout how long to allow the futures to run before throwing exception
* #param timeUnit unit of timeout
* #param allowedExceptions how many of the futures do we tolerate exceptions,
* NOTE: if an exception is thrown from the futures it will return null, until it reaches the allowedExceptions threshold
* */
public static <T> List<T> extractFromFutures(List<CompletableFuture<T>> futures, int timeout, TimeUnit timeUnit, int allowedExceptions) {
CompletableFuture<Void> allDoneFuture = CompletableFuture
.allOf(futures.toArray(new CompletableFuture[futures.size()]));
try {
AtomicInteger exceptionCount = new AtomicInteger(0);
return allDoneFuture.thenApply(v ->//when all are done
futures.stream().
map(future -> {
try {
//if only I could set an individual timeout
return future.get(timeout, timeUnit);
} catch (Exception e) {
future.cancel(true);
int curExceptionCnt = exceptionCount.incrementAndGet();
if(curExceptionCnt >= allowedExceptions){
//I would've hoped that it will throw it to the calling try-catch
//and then cancel all futures, but it doesn't
throw new RuntimeException(e);
}
else{
return null;
}
}
}).
collect(Collectors.<T>toList())
).get(timeout, timeUnit);
} catch (Exception e) {
allDoneFuture.cancel(true);
throw new RuntimeException(e);
}
}
To cancel all of the remaining futures after a certain number of exceptions you can call exceptionally on each of them and increment the exception count and possibly cancel them inside of that.
For individual timeouts you could create a class that holds the future with its timeout then sort them based on the timeout and call get with the timeout minus the elapsed time.
static class FutureWithTimeout<T> {
CompletableFuture<T> f;
long timeout;
TimeUnit timeUnit;
FutureWithTimeout(CompletableFuture<T> f, long timeout, TimeUnit timeUnit) {
this.f = f;
this.timeout = timeout;
this.timeUnit = timeUnit;
}
}
public static <T> List<T> extractFromFutures(List<FutureWithTimeout<T>> futures, int allowedExceptions) {
AtomicInteger exceptionCount = new AtomicInteger(0);
futures.forEach(f -> f.f.exceptionally(t -> {
if(exceptionCount.getAndIncrement() == allowedExceptions){
futures.forEach(c -> c.f.cancel(false));
}
return null;
}));
long t = System.nanoTime();
return futures.stream()
.sorted(Comparator.comparingLong(f -> f.timeUnit.toNanos(f.timeout)))
.map(f -> {
try {
return f.f.get(Math.max(0, f.timeUnit.toNanos(f.timeout) - (System.nanoTime() - t)),
TimeUnit.NANOSECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException ex) {
f.f.cancel(false);
return null;
}
})
.collect(Collectors.toList());
}
Note that this may return the list in a different order than it was passed in. If you need it in the same order then you could change the map().collect() to a forEachOrdered and then re map them into their results after without sorting.
Also the mayInterruptIfRunning parameter to cancel has no effect on CompletableFuture so I changed it to false.
CompletableFuture completely ignores any call to cancel(true). I don't know why (presumably to simplify the API), but it sucks. If you want to make futures actually cancelable (where you can either manually check for interruption, or accept cancellation by blocking on a lock), then you have to use Future, not CompletableFuture.
Related
I have code that should execute reasonably fast but occasionally may take long time to execute and produce a result. I'd like to limit the duration of that operation and abort it if it runs longer than a given time limit. I'd like the code to look like
Supplier<T> longRunningFoo = () -> {...}; // this may take a while to run
LongOpRunner runner = new LongOpRunner(longRunningFoo); // <-- some wrapper that limits operation duration
try {
T result = runner.call(10000 /* ms */); // abort after 10000 milliseconds
} catch (LongOpTimeout e) {
// handle timeout exception when "foo" gets aborted
}
Before I start writing my own I am interested to see if there are existing libraries that provide this capability.
You can use Java provided built in Future where it provides the facility for timeout. See below the small code snippet.
ExecutorService ex = Executors.newSingleThreadExecutor();
Future<?> future = ex.submit(new Runnable() {
public void run() {
try {
//Do some long running operations
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Completed operation");
}
});
Object someObject = future.get(10, TimeUnit.SECONDS);
You can also refer below the link for further reference.
You can use ExecutorService.submit(Callable task) and then call Future.get(long timeout, TimeUnit unit) on the result.
I have method that is checking the CompletableFuture execution time. If such CompletableFuture is executing for more than 2 seconds i want to kill this task. But how can I doit if i don't have control overy thread where CompletableFuture methods are executed ?
final CompletableFuture<List<List<Student>>> responseFuture = new CompletableFuture<>();
responseFuture.supplyAsync(this::createAllRandomGroups)
.thenAccept(this::printGroups)
.exceptionally(throwable -> {
throwable.printStackTrace();
return null;
});
createAllRandomGroups()
private List<List<Student>> createAllRandomGroups() {
System.out.println("XD");
List<Student> allStudents = ClassGroupUtils.getActiveUsers();
Controller controller = Controller.getInstance();
List<List<Student>> groups = new ArrayList<>();
int groupSize = Integer.valueOf(controller.getGroupSizeComboBox().getSelectionModel().getSelectedItem());
int numberOfGroupsToGenerate = allStudents.size() / groupSize;
int studentWithoutGroup = allStudents.size() % groupSize;
if (studentWithoutGroup != 0) groups.add(this.getListOfStudentsWithoutGroup(allStudents, groupSize));
for(int i = 0; i < numberOfGroupsToGenerate; i++) {
boolean isGroupCreated = false;
while (!isGroupCreated){
Collections.shuffle(allStudents);
List<Student> newGroup = this.createNewRandomGroupOfStudents(allStudents, groupSize);
groups.add(newGroup);
if (!DataManager.isNewGroupDuplicated(newGroup.toString())) {
isGroupCreated = true;
allStudents.removeAll(newGroup);
}
}
}
DataManager.saveGroupsToCache(groups);
return groups;
}
printGroups()
private void printGroups(List<List<Student>> lists) {
System.out.println(lists);
}
This statement responseFuture.cancel(true); does not kill thread where responseFuture is doing the methods. So what is the most elegant way to terminate CompletableFuture thread ?
When you create a chain of CompletableFuture stages like b = a.thenApply(function), this handy method creates a setup of different components. Basically, these components refer to each other as a → function → b, so the completion of a will trigger the evaluation of function which will first pre-check whether b still is not completed, then evaluate your function and attempt to complete b with the result.
But b itself has no knowledge of function or the thread that will evaluate it. In fact, function is not special to b, anyone could call complete, completeExceptionally or cancel on it from any thread, the first one winning. Hence, the completable in the class name.
The only way to get hands on the threads evaluating the functions, is to be in control of them right from the start, e.g.
ExecutorService myWorkers = Executors.newFixedThreadPool(2);
CompletableFuture<FinalResultType> future
= CompletableFuture.supplyAsync(() -> generateInitialValue(), myWorkers)
.thenApplyAsync(v -> nextCalculation(v), myWorkers)
.thenApplyAsync(v -> lastCalculation(v), myWorkers);
future.whenComplete((x,y) -> myWorkers.shutdownNow());
Now, the completion of future, e.g. via cancellation, will ensure that no new evaluation will be triggered by this chain and further makes an attempt to interrupt ongoing evaluations, if any.
So you can implement a timeout, e.g.
try {
try {
FinalResultType result = future.get(2, TimeUnit.SECONDS);
System.out.println("got "+result);
}
catch(TimeoutException ex) {
if(future.cancel(true)) System.out.println("cancelled");
else System.out.println("got "+future.get());
}
}
catch(ExecutionException|InterruptedException ex) {
ex.printStackTrace();
}
Not that the rejection of tasks due to the shutdown of the thread pool may cause some of the intermediate future to never complete, but for this chain of stages, this is irrelevant. All that matters, is, that the final stage future is completed, which is guaranteed, as it is its completion which triggers the shutdown.
The only way to terminate a thread is via interruption, which is a cooperative mechanism. This means the the thread must implement interruption logic, by handling the InterruptedException.
But it is a really bad practice to interrupt threads that you don't own, which I think is your case.
I have method that is checking the CompletableFuture execution time. If such CompletableFuture is executing for more than 2 seconds i want to kill this task. But how can I doit if i don't have control overy thread where CompletableFuture methods are executed ?
final CompletableFuture<List<List<Student>>> responseFuture = new CompletableFuture<>();
responseFuture.supplyAsync(this::createAllRandomGroups)
.thenAccept(this::printGroups)
.exceptionally(throwable -> {
throwable.printStackTrace();
return null;
});
createAllRandomGroups()
private List<List<Student>> createAllRandomGroups() {
System.out.println("XD");
List<Student> allStudents = ClassGroupUtils.getActiveUsers();
Controller controller = Controller.getInstance();
List<List<Student>> groups = new ArrayList<>();
int groupSize = Integer.valueOf(controller.getGroupSizeComboBox().getSelectionModel().getSelectedItem());
int numberOfGroupsToGenerate = allStudents.size() / groupSize;
int studentWithoutGroup = allStudents.size() % groupSize;
if (studentWithoutGroup != 0) groups.add(this.getListOfStudentsWithoutGroup(allStudents, groupSize));
for(int i = 0; i < numberOfGroupsToGenerate; i++) {
boolean isGroupCreated = false;
while (!isGroupCreated){
Collections.shuffle(allStudents);
List<Student> newGroup = this.createNewRandomGroupOfStudents(allStudents, groupSize);
groups.add(newGroup);
if (!DataManager.isNewGroupDuplicated(newGroup.toString())) {
isGroupCreated = true;
allStudents.removeAll(newGroup);
}
}
}
DataManager.saveGroupsToCache(groups);
return groups;
}
printGroups()
private void printGroups(List<List<Student>> lists) {
System.out.println(lists);
}
This statement responseFuture.cancel(true); does not kill thread where responseFuture is doing the methods. So what is the most elegant way to terminate CompletableFuture thread ?
When you create a chain of CompletableFuture stages like b = a.thenApply(function), this handy method creates a setup of different components. Basically, these components refer to each other as a → function → b, so the completion of a will trigger the evaluation of function which will first pre-check whether b still is not completed, then evaluate your function and attempt to complete b with the result.
But b itself has no knowledge of function or the thread that will evaluate it. In fact, function is not special to b, anyone could call complete, completeExceptionally or cancel on it from any thread, the first one winning. Hence, the completable in the class name.
The only way to get hands on the threads evaluating the functions, is to be in control of them right from the start, e.g.
ExecutorService myWorkers = Executors.newFixedThreadPool(2);
CompletableFuture<FinalResultType> future
= CompletableFuture.supplyAsync(() -> generateInitialValue(), myWorkers)
.thenApplyAsync(v -> nextCalculation(v), myWorkers)
.thenApplyAsync(v -> lastCalculation(v), myWorkers);
future.whenComplete((x,y) -> myWorkers.shutdownNow());
Now, the completion of future, e.g. via cancellation, will ensure that no new evaluation will be triggered by this chain and further makes an attempt to interrupt ongoing evaluations, if any.
So you can implement a timeout, e.g.
try {
try {
FinalResultType result = future.get(2, TimeUnit.SECONDS);
System.out.println("got "+result);
}
catch(TimeoutException ex) {
if(future.cancel(true)) System.out.println("cancelled");
else System.out.println("got "+future.get());
}
}
catch(ExecutionException|InterruptedException ex) {
ex.printStackTrace();
}
Not that the rejection of tasks due to the shutdown of the thread pool may cause some of the intermediate future to never complete, but for this chain of stages, this is irrelevant. All that matters, is, that the final stage future is completed, which is guaranteed, as it is its completion which triggers the shutdown.
The only way to terminate a thread is via interruption, which is a cooperative mechanism. This means the the thread must implement interruption logic, by handling the InterruptedException.
But it is a really bad practice to interrupt threads that you don't own, which I think is your case.
Suppose I have a Stream<Callable<SomeClass>> stream;. The stream is accessing over a million objects which will not fit in memory.
What is the idiomatic way to convert this to a Stream<SomeClass> in a manner that ensures the Callable::call are executed in parallel before being delivered to a consumer that is non-threaded-safe (perhaps by calling .sequential().forEach() or some other bottlenecking mechanism)?
i.e. Process the stream in parallel but deliver the output sequentially (random order ok, as long as it's single-threaded).
I know I could do what I want by setting up an ExecutionService and a Queue between the original stream and the consumer. But that seems like a lot of code, is there a magic one-liner?
You could still employ an ExecutorService for parallelization. Like this:
ExecutorService service = Executors.newFixedThreadPool(4);
stream.map(c -> service.submit(c)).map(future -> {
try {
return future.get(); //retrieve callable result
} catch (InterruptedException | ExecutionException ex) {
//Exception handling
throw new RuntimeException(ex);
}
});
You can process the resulting Stream<SomeClass> further sequentially.
If you use forEach/forEachOrdered directly on the Stream<Future<SomeClass>> you can process a resulting SomeClass-object directly once the current future is done (different from when you use invokeAll() which blocks until every task is done).
If you want to process the results of the callables in the exact order they are available you will have to use CompletionService which can't be used along with a single chain of stream operations due to the necessary call of Future<SomeClass> f = completionService.take() after submitting the callables.
EDIT:
Using an ExecutorService within streams doesn't work the way I showed above, because every Callable is submitted and requested via future.get() one after the other.
I found a possible even side-effect heavier solution dividing the Callables in fixed parallelized chunks.
I use a class TaskMapper as mapping-function for submitting the Callables and mapping them to chunks:
class TaskMapper implements Function<Callable<Integer>, List<Future<Integer>>>{
private final ExecutorService service;
private final int chunkSize;
private List<Future<Integer>> chunk = new ArrayList<>();
TaskMapper(ExecutorService service, int chunkSize){
this.service = service;
this.chunkSize = chunkSize;
}
#Override
public List<Future<Integer>> apply(Callable<Integer> c) {
chunk.add(service.submit(c));
if(chunk.size() == chunkSize){
List<Future<Integer>> fList = chunk;
chunk = new ArrayList<>();
return fList;
}else{
return null;
}
}
List<Future<Integer>> getChunk(){
return chunk;
}
}
This how the chain of stream-operations looks like:
ExecutorService service = Executors.newFixedThreadPool(4);
TaskMapper taskMapper = new TaskMapper(service, 4);
stream.map(taskMapper)
.filter(fl -> fl != null) //filter for the chunks
.flatMap(fl -> fl.stream()) //flat-map the chunks to futures
.map(future -> {
try {
return future.get();
} catch (InterruptedException | ExecutionException ex) {
throw new RuntimeException(ex);
}
});
//process the remaining futures
for(Future<Integer> f : taskMapper.getChunk()){
try {
Integer i = f.get();
//process i
} catch (InterruptedException | ExecutionException ex) {
//exception handling
}
}
This works as follows: The TaskMapper takes 4 callables each time submits them to the service and maps them to a chunk of futures (without Spliterator). This is solved by mapping to null for the 1st, 2nd and 3rd callable each time. null could be replaced by a dummy object for example. The mapping function that maps the futures to the results waits for the result of each future of the chunk. I use Integer in my example instead of SomeClass. When all results of the futures in the current chunk are mapped, a new chunk will be created and parallelized. Finally, if the number of elements in the stream is not dividable by the chunkSize(4 in my example), the remaining futures will have to be retrieved from the TaskMapper and processed outside of the stream.
This construct works for the tests I carried out, but I am aware that it is possible fragile due to the side-effects, statefullness and the undefined evaluation behavior of the stream.
EDIT2:
I made a version of the construct from the previous EDIT using a custom Spliterator:
public class ExecutorServiceSpliterator<T> extends AbstractSpliterator<Future<T>>{
private final Spliterator<? extends Callable<T>> srcSpliterator;
private final ExecutorService service;
private final int chunkSize;
private final Queue<Future<T>> futures = new LinkedList<>();
private ExecutorServiceSpliterator(Spliterator<? extends Callable<T>> srcSpliterator) {
this(srcSpliterator, Executors.newFixedThreadPool(8), 30); //default
}
private ExecutorServiceSpliterator(Spliterator<? extends Callable<T>> srcSpliterator, ExecutorService service, int chunkSize) {
super(Long.MAX_VALUE, srcSpliterator.characteristics() & ~SIZED & ~CONCURRENT);
this.srcSpliterator = srcSpliterator;
this.service = service;
this.chunkSize = chunkSize;
}
public static <T> Stream<T> pipeParallelized(Stream<? extends Callable<T>> srcStream){
return getStream(new ExecutorServiceSpliterator<>(srcStream.spliterator()));
}
public static <T> Stream<T> pipeParallelized(Stream<? extends Callable<T>> srcStream, ExecutorService service, int chunkSize){
return getStream(new ExecutorServiceSpliterator<>(srcStream.spliterator(), service, chunkSize));
}
private static <T> Stream<T> getStream(ExecutorServiceSpliterator<T> serviceSpliterator){
return StreamSupport.stream(serviceSpliterator, false)
.map(future -> {
try {
return future.get();
} catch (InterruptedException | ExecutionException ex) {
throw new RuntimeException(ex);
}
}
);
}
#Override
public boolean tryAdvance(Consumer<? super Future<T>> action) {
boolean didAdvance = true;
while((didAdvance = srcSpliterator.tryAdvance(c -> futures.add(service.submit(c))))
&& futures.size() < chunkSize);
if(!didAdvance){
service.shutdown();
}
if(!futures.isEmpty()){
Future<T> future = futures.remove();
action.accept(future);
return true;
}
return false;
}
}
This class provides functions (pipeParallelized()) which take a stream of Callable-elements execute them chunk-wise in parallel and then ouput a sequential stream containing the results. Spliterators are allowed to be stateful. Therefore this version should hopefully not violate any stream operation constraints. This is how the Splitterator can be used (close to a "magic oneliner"):
ExecutorServiceSpliterator.pipeParallelized(stream);
This line takes the stream of Callables stream parallelizes the execution of them and returns a sequential stream containing the results (piping happens lazily -> should work with millions of callables) which can be processed further with regular stream operations.
The implementation of ExecutorServiceSpliteratoris very basic. It should mainly demonstrate how it could be done in principle. The resupplying of the service and the retrieving of the results could be optimized. For example if the resulting stream is allowed to be unordered, a CompletionService could be used.
You are asking for an idiomatic solution. Streams with sideeffects in its behavioral parameters are discouraged (explicitly stated in the javadoc of Stream).
So the idiomatic solution is basically ExecutorService + Futures and some loops/forEach(). If you have a Stream as parameter, just transform it to a List with the standard Collector.
Something like that:
ExecutorService service = Executors.newFixedThreadPool(5);
service.invokeAll(callables).forEach( doSomething );
// or just
return service.invokeAll(callables);
First Example:
ExecutorService executor = Executors.newWorkStealingPool();
List<Callable<String>> callables = Arrays.asList(
() -> "job1",
() -> "job2",
() -> "job3");
executor.invokeAll(callables).stream().map(future -> {
return future.get();
}).forEach(System.out::println);
Second Example:
Stream.of("1", "2", "3", "4", "", "5")
.filter(s->s.length() > 0)
.parallel()
.forEachOrdered(System.out::println);
public static void main(String[] args) {
testInfititeCallableStream();
}
private static void testInfititeCallableStream() {
ExecutorService service = Executors.newFixedThreadPool(100);
Consumer<Future<String>> consumeResult = (Future<String> future)->{
try {
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
};
getCallableStream().parallel().map(callable -> service.submit(callable)).forEach(consumeResult);
}
private static Stream<Callable<String>> getCallableStream() {
Random randomWait = new Random();
return Stream.<Callable<String>>generate(() ->
new Callable<String>() {
public String call() throws Exception {
//wait for testing
long time = System.currentTimeMillis();
TimeUnit.MILLISECONDS.sleep(randomWait.nextInt(5000));
return time + ":" +UUID.randomUUID().toString();
};
}).limit(Integer.MAX_VALUE);
}
None of the other answers worked for me.
I finally settled on something like this (pseudo-code):
ExecutorService executor = Executors.newWorkStealingPool();
CompletionService completor = new CompletionService(executor);
int count = stream.map(completor::submit).count();
while(count-- > 0) {
SomeClass obj = completor.take();
consume(obj);
}
The consume(obj) loop is executed sequentially in a single thread while the individual callable tasks asynchronously work their way through the CompletionService's multiple threads. Memory consumption is limited as the CompletionService will have only as many items in progress at a time as there are threads available. The Callables waiting for execution are eagerly materialized from the stream, but the impact of that is negligible compared to the memory each consumes once it starts executing (your use-case may vary).
Consider the following code:
public static void main(String ... args) throws InterruptedException {
ScheduledExecutorService threadsPool = Executors.newSingleThreadScheduledExecutor();
Future<?> f = threadsPool.scheduleAtFixedRate(() -> {
try {
Thread.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
}, 0, 2, TimeUnit.SECONDS);
Thread.sleep(5000);
threadsPool.shutdown();
System.out.println(threadsPool.awaitTermination(1, TimeUnit.SECONDS));
try {
f.get();
} catch (Exception e) {
e.printStackTrace();
}
}
Note the sleeping time of the evil thread: 1ms. This guarantees that the shutdown will be while the threads pool is waiting for the next iteration and the evil thread is not running.
This results in a a CancellationException on the get() method: If I understand correctly, ScheduledExecutorService cancels any pending task when calling shutdown(), so this behavior makes sense.
Next I have changed the sleeping time of evil thread from 1 to 1999. This guarantees that the shutdown will be during the sleeping of the evil thread.
This results in waiting forever on the get() method.
My next question is why is this behavior happening? Calling shutdown will gracefully shutdown the service. Indeed, the evil thread finishes the iteration and doesn't start again.
But why doesn't the get() method return? Am I misunderstanding the get() method on ScheduledFuture?
I thought that as soon as the evil thread finishes, and the pool is shutdown, the get() method should return null.
If the future did not complete then it is possible that the future.cancel() method was invoked by the shutdown method (or the executor somehow cancelled the future).
This is the expected behavior of requesting the executor to shutdown, since it does not wait for tasks to complete.
If the future has been cancelled before finishing, throwing the CancellationException is the expected behavior. Otherwise it would wait for ether for the task to return.
In this case, since you use ScheduledExecutorService you can use the ScheduledFuture instead of Future in this case to get more information. https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledFuture.html
This will allow you to access the getDelay method provided by the Delayed interface.
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Delayed.html
If you also check the source code of the method scheduleAtFixedRate you will see that it actually creates a ScheduledFutureTask. See code below. (Copy from source.)
/**
* #throws RejectedExecutionException {#inheritDoc}
* #throws NullPointerException {#inheritDoc}
* #throws IllegalArgumentException {#inheritDoc}
*/
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (period <= 0)
throw new IllegalArgumentException();
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
return t;
}
The ScheduledFutureTask's run method automatically reexecutes it as soon as it is finished. (see last line of source code).
/**
* Overrides FutureTask version so as to reset/requeue if periodic.
*/
public void run() {
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic)
ScheduledFutureTask.super.run();
else if (ScheduledFutureTask.super.runAndReset()) {
setNextRunTime();
reExecutePeriodic(outerTask);
}
}
The outerTask is this so it is actually the ScheduledFutureTask itself. So it is never complete. For this reason you can never actually get the result.