Home > OS >  Why ExecutorService is not shutdown after invokeAny returns a result?
Why ExecutorService is not shutdown after invokeAny returns a result?

Time:03-17

As I was experimenting with Future and Callable in Java, I had a result I cannot make sense of with my current understanding. The below scenario is for demonstration purposes only.

From my understanding, when a collection of Callables are submitted to an ExecutorService via an invokeAny() call, ExecutorService returns the first result it gets out of them. After the retrieval of the result, I call executorService.shutDown() which I expected to shutDown the executorService as it already return a result. However, for the below scenario the program halts. I have also tried calling executorService.shutDownNow() but no luck.

I've tried the same example using two methods that only sleeps the their thread for 1000 and 5000 seconds respectively, and after the first one is done sleeping executorService.shutDown() worked as expected.

My only guess is that shutDown() or shutDownNow() cannot interrupt an already executing thread just like cancel(mayInterruptIfRunning=true) does.

Could someone please explain what's going on here, am I guessing this correctly?

Here's the example:

public class InvokeAnyExample {
    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        int N = 50; // If I set N = 10, program exits successfully.

        ExecutorService executorService = Executors.newFixedThreadPool(2);

        Callable<Long> naiveFibonacci = () -> {
            log("Naive Fibonacci");
            long result = fibonacciNaive(N);
            log("Naive Fibonacci Finished");
            return result;
        };

        Callable<Long> optimizedFibonacci = () -> {
            log("Optimized Fibonacci");
            long[] cache = new long[1000];
            Arrays.fill(cache, -1);
            long result = fibonacciOptimized(N, cache);
            log("Optimized Fibonacci Finished");
            return result;
        };

        Long result = executorService.invokeAny(Arrays.asList(naiveFibonacci, optimizedFibonacci), 5, TimeUnit.SECONDS);
        log(String.valueOf(result));

        executorService.shutdown();
//        executorService.shutdownNow();
    }

    private static long fibonacciNaive(int n) {
        if (n == 0 || n == 1) return n;

        return fibonacciNaive(n - 1)   fibonacciNaive(n - 2);
    }

    private static long fibonacciOptimized(int n, long[] cache) {
        if (n == 0 || n == 1) return n;
        if (cache[n] != -1) return cache[n];

        long result = fibonacciOptimized(n - 1, cache)   fibonacciOptimized(n - 2, cache);
        cache[n] = result;

        return result;
    }

    private static void log(String message) {
        String prefix = Thread.currentThread().getName();
        System.out.println(String.format("In Thread (%s) : %s", prefix, message));
    }
}

Here's the output:

In Thread (pool-1-thread-2) : Optimized Fibonacci
In Thread (pool-1-thread-1) : Naive Fibonacci
In Thread (pool-1-thread-2) : Optimized Fibonacci Finished
In Thread (main) : 12586269025
--- PROGRAM HALTS

CodePudding user response:

As Mark Rotteveel wrote in his comment:

The naiveFibonacci task is not interruptible. It does not call any methods that check the interrupted state of the thread nor does fibonacciNaive() method itself check the interrupted state of the thread.

If you want to make fibonacciNaive() method interruptible you could change it to:

private static long fibonacciNaive(int n) throws InterruptedException {
    if (n == 0 || n == 1) return n;

    if (Thread.currentThread().isInterrupted()) {
        log("interrupt detected");
        throw new InterruptedException();
    }

    return fibonacciNaive(n - 1)   fibonacciNaive(n - 2);
}

And with that change the naiveFibonacci task will be stopped as soon as optimizedFibonacci produces a result.

  • Related