Stack Overflow Asked by daniu on February 2, 2021
I’m trying to use my own ExecutorService
to create a set of CompletableFuture
s to chain several process steps. These steps might throw exceptions.
When they do, it seems to me the thread in the ExecutorService
is not released although I’m trying to handle this case.
class Scratch {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(10);
AtomicInteger counter = new AtomicInteger();
Supplier<?> throwingException = () -> { throw new RuntimeException("throw " + counter.incrementAndGet()); };
Function<String, CompletableFuture<?>> process =
url -> CompletableFuture.supplyAsync(throwingException, executor)
.exceptionally(Scratch::log);
var collect = IntStream.range(1, 10).mapToObj(i -> "url" + i)
.map(process)
.toArray(CompletableFuture[]::new);
final CompletableFuture<Void> together = CompletableFuture.allOf(collect);
System.out.println("joining");
together.exceptionally(Scratch::log).join();
System.out.println("finished");
if (executor.awaitTermination(5, TimeUnit.SECONDS)) {
System.out.println("exiting cleanly");
} else {
System.out.println("not terminated");
}
executor.submit(() -> System.out.println("still executing"));
}
static <T> T log(Throwable t) {
System.out.println(t.getMessage());
return null;
}
}
Output is
java.lang.RuntimeException: throw 1
joining
java.lang.RuntimeException: throw 2
java.lang.RuntimeException: throw 3
java.lang.RuntimeException: throw 4
java.lang.RuntimeException: throw 5
java.lang.RuntimeException: throw 6
java.lang.RuntimeException: throw 7
java.lang.RuntimeException: throw 8
java.lang.RuntimeException: throw 9
finished
not terminated
The process started by this also isn’t terminated (which is how I noticed).
It seems to me this should mean there are no threads left in the ExecutorService
at this point, but that doesn’t seem to be the case; if we lower the thread pool capacity, it will still run all submitted tasks, and if we add submit another after the failed termination (eg executor.submit(() -> System.out.println("still executing"));
), it will get executed.
If we don’t pass our own ExecutorService
to the CompletableFutre::supplyAsync
, the process will terminate as expected.
I also tried other versions of handling the exceptional state (like using together.whenComplete()
), but that has the same result.
Why is this happening, and how can I make sure the ExecutorService
terminates correctly?
EDIT: I realized that it’s not the exception that’s causing the problem, this will occur with any task provided to CompletableFuture
with your own executor service, which makes total sense given Eugene’s reply. I’m changing the question title.
There are two things going on here. First one is that when you execute without an explicit Executor
, your actions run in the ForkJoinPool
. That pool uses daemon-threads, which do not stop the VM to exit. So when your main
is over, VM exists.
The second point is in the documentation of awaitTermination
, actually:
Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.
Since you did not call shutDown
and that pool creates non-daemon threads, the process does not exit.
Answered by Eugene on February 2, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP