Search code examples
javacompletable-future

CompletableFuture miss-understanding


I seem to fail miserably at an apparently simple example with CompletableFuture.

I have a CompletableFuture that is blocked in a different thread, something like:

        Supplier<CompletableFuture<String>> one = () -> CompletableFuture.supplyAsync(() -> {
            System.out.println("started cf");
            LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(3));
            System.out.println("done cf");
            return "";
        });
        ExecutorService service = Executors.newSingleThreadExecutor();
        service.submit(() -> {
            try {
                future = one.get();
                future.get();
            } catch (Exception e) {
                System.out.println("caught");
                future.completeExceptionally(e);
            }
        });

I want to be able to cancel it, and then join to wait until it is completed in a thread that is different then the one above, so something like this:

    static CompletableFuture<String> future;

    public static void main(String[] args) {

        Supplier<CompletableFuture<String>> one = () -> CompletableFuture.supplyAsync(() -> {
            System.out.println("started cf");
            LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(3));
            System.out.println("done cf");
            return "";
        });
        ExecutorService service = Executors.newSingleThreadExecutor();
        service.submit(() -> {
            try {
                future = one.get();
                future.get();
            } catch (Exception e) {
                System.out.println("caught");
                future.completeExceptionally(e);
            }
        });

        LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
        future.cancel(true);
        System.out.println("before join");
        String s = future.join();
        System.out.println("s value : " + s);
        System.out.println("done");
    }

To my surprise, this one String s = future.join(); blocks and I don't understand why.

More surprising, if I change my code to (instead of String s = future.join();):

        String s = null;
        try {
            s = future.join();
        } catch(Exception e) {
            System.out.println("here : " + e.getClass());
        }

this one does not block. Can someone please shed some light here?


Solution

  • With or without the try/catch around future.join() this blocks for me in the same way, whereby I define "blocking" here as: I put your code into a file, run it from the command line and the process does not finish.

    The reason is that Executors.newSingleThreadExecutor() works with a non-daemon thread, meaning the pool's thread keeps on running even when the main() method returns (you should see your "done" output). Consequently the program's process does not finish.

    You may either

    • call service.shutdown() or some of its variants before future.join() or
    • use the variant newSingleThreadExecutor(ThreadFactory) where the thread factory provides a daemon thread (.setDaemon(true))