Future<T>.get() stuck when the runnable throws exception--while running on separate executor service?

Viewed 660

A repro here:

import java.util.concurrent.*;

public class Main {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("Hello!");

        ExecutorService exec = Executors.newSingleThreadExecutor();

        Future<Integer> f = exec.submit(() -> x());

        f.get();

        System.out.println("f.get() returned");

        exec.shutdownNow();

        System.out.println("Good bye!");
    }

    private static Integer x() {
        throw new RuntimeException("An unfortunate event");
    }
}

The output only shows "Hello!" and the exception stacktrace, then gets the program hangs forever.

The changes below kind of work around the problem, but any idea why the execution hangs in the code above?

Using the common thread pool does NOT hang:

Future<Integer> f = ForkJoinPool.commonPool().submit(() -> x());

Wrapping the call around try/catch lets the application exit normally:

Future<Integer> f = exec.submit(() -> x());

try {
    f.get();
} catch (Exception ex) {
    ex.printStackTrace();
}
1 Answers

It's always hard to find a good example of try-finally and its appropriate usage. I think it's the case.

try {
    f.get();
    System.out.println("f.get() returned");
} finally {
    exec.shutdownNow();
}

The exception thrown from f.get(); wasn't handled, the main thread failed. But the application still contains non-daemon threads manageable by the ExecutorService to which you have no direct access.

Related