Reputation: 7321
I'm running two instances of CompletableFuture
, which wait 1 second and print something to the console. The first one I interrupt after 0.5 seconds. So I expect only the second one to print, but in fact both do. What's going on here?
Here's the code:
CompletableFuture<Void> c1 = CompletableFuture.runAsync(() -> {
System.out.println("Start CF 1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(1);
});
CompletableFuture<Void> c2 = CompletableFuture.runAsync(() -> {
System.out.println("Start CF 2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(2);
});
long start = System.currentTimeMillis();
try {
c1.get(500, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
System.out.println("CF interrupted after " + (System.currentTimeMillis() - start) + "ms");
}
c2.get();
And it prints:
Start CF 1
Start CF 2
CF interrupted after 510ms
2
1
Upvotes: 0
Views: 2575
Reputation: 298153
The timeout for the get
method only tells, when the execution of the get
method should be stopped. It does not affect the computation of the result. This applies to all futures.
So, to interrupt a task, you would need to call cancel(true)
but in case of CompletableFuture
not even that will interrupt the task:
Parameters:
mayInterruptIfRunning
- this value has no effect in this implementation because interrupts are not used to control processing.”
If you want interruptible tasks, you need to use an ExecutorService
, but you can also use the same thread pool as CompletableFuture
’s default executor:
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService es = ForkJoinPool.commonPool();
Future<Void> c1 = es.submit(() -> {
System.out.println("Start CF 1");
Thread.sleep(1000);
System.out.println(1);
return null;
});
Future<Void> c2 = es.submit(() -> {
System.out.println("Start CF 2");
Thread.sleep(1000);
System.out.println(2);
return null;
});
long start = System.currentTimeMillis();
try {
c1.get(500, TimeUnit.MILLISECONDS);
} catch(TimeoutException e) {
c1.cancel(true);
System.out.println("timeout after "
+ (System.currentTimeMillis() - start) + "ms, canceled");
}
c2.get();
}
Note that the interruption may still be too slow to prevent the printing of 1
. On my machine, I needed at least Thread.sleep(1100);
to interrupt the task before the printing.
Upvotes: 1
Reputation: 44150
Why do you think it should? What if 2 consumers want to handle the result of the same future, and one is prepared to wait longer than the other? The first would kill it for the second, which it might've completed in time for.
In any case, Java doesn't have the concept of killing a task. The best you can do is set a flag asking it to stop - an interrupt - but that relies on the task checking and respecting the flag. Your tasks do, since Thread.sleep()
implicitly checks the interrupt flag, but it's a contrived example, and a lot of tasks wouldn't ever check it.
Upvotes: 2