为什么 ExecutorService 在 invokeAny returns 结果后没有关闭?

Why ExecutorService is not shutdown after invokeAny returns a result?

当我在 Java 中试验 Future 和 Callable 时,我得到了一个以我目前的理解无法理解的结果。 以下场景仅供演示。

据我了解,当通过 invokeAny() 调用将 Callable 的集合提交给 ExecutorService 时,第一个 ExecutorService returns结果它从他们身上消失了。检索结果后,我调用 executorService.shutDown(),我希望它关闭 executorService,因为它已经 return 结果。但是,对于以下情况,程序会停止。我也试过打电话 executorService.shutDownNow() 但没有成功。

我已经使用两种方法尝试了相同的示例,这两种方法分别只让它们的线程休眠 10005000 秒,并且在第一个完成休眠后 executorService.shutDown() 工作不出所料。

我唯一的猜测是 shutDown()shutDownNow() 不能像 cancel(mayInterruptIfRunning=true) 那样中断已经执行的线程。

谁能解释一下这是怎么回事,我猜对了吗?

示例如下:

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));
    }
}

这是输出:

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

正如 Mark Rotteveel 在他的评论中所写:

naiveFibonacci 任务不可中断。它不调用任何检查线程中断状态的方法,fibonacciNaive() 方法本身也不检查线程中断状态。

如果您想使 fibonacciNaive() 方法可中断,您可以将其更改为:

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

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

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

随着这一改变,naiveFibonacci 任务将在 optimizedFibonacci 产生结果后立即停止。

更改:正如 Holger 评论的那样,使用 Thread.interrupted() 优于 Thread.currentThread().isInterrupted(),因为 if 语句中的代码将处理中断。