使用 Callable 和 Future 时程序不会退出

Posted

技术标签:

【中文标题】使用 Callable 和 Future 时程序不会退出【英文标题】:Program won't exit in using Callable and Future 【发布时间】:2021-11-16 23:57:51 【问题描述】:

IDE:IntelliJ JDK:Java 11

在测试讲座中的示例代码时,我发现了一些非常奇怪的东西,即使没有任何循环,我的程序也不会停止!

import java.util.concurrent.Callable;

public class FindMaxTask implements Callable<Integer> 
    private int[] data;
    private int start;
    private int end;

    public FindMaxTask(int[] data, int start, int end) 
        this.data = data;
        this.start = start;
        this.end = end;
    

    public Integer call() 
        int max = Integer.MIN_VALUE;
        for (int i = start; i < end; i++) 
            if (data[i] > max) 
                max = data[i];
            
        
        return max;
    

这是一个实现Callable 接口的FindMaxTask,它在给定的数组范围内执行查找最大值。

public static void testCallable() throws ExecutionException, InterruptedException 
        final int dataSize = 10000;
        int[] data = new int[dataSize];
        Random random = new Random();

        for (int i = 0; i < dataSize; i++) 
            data[i] = random.nextInt(1000000);
        

        FindMaxTask task0 = new FindMaxTask(data, 0, dataSize / 2);
        FindMaxTask task1 = new FindMaxTask(data, dataSize / 2, dataSize);

        ExecutorService service = Executors.newFixedThreadPool(2);

        Future<Integer> future0 = service.submit(task0);
        Future<Integer> future1 = service.submit(task1);

        int ret0 = future0.get();
        int ret1 = future1.get();

        System.out.println("ret0: " + ret0 + System.lineSeparator() + "ret1: " + ret1);
    

这是 Main 类中的 testCallable 静态函数。

如果我在 main 函数中运行 testCallable 函数,程序会在控制台中打印每个 ret 值后停止。

这个问题与CallableFuture有关吗? (我在打印 ret0,ret1 后尝试调试 future1.isDone() 的值为 true,所以显然 chlid 线程似乎没有被阻塞) 请告诉我为什么会发生这种情况

【问题讨论】:

你必须关闭线程池。否则,您仍然有非守护线程在后台运行。在所有非守护线程都停止之前,JVM 不会退出。 @DavidConrad 我以为主线程退出后子线程会消失,这是JVM的特殊功能吗? 子线程只有在主线程退出后才会消失,如果它们是守护线程。从 Java 1.0 开始就是如此。见Thread Executors.newFixedThreadPool 被记录为“池中的线程将一直存在,直到它被明确关闭。”。 【参考方案1】:

你必须关闭线程池。否则,您仍然有非守护线程在后台运行。在所有非守护线程都停止之前,JVM 不会退出。

ExecutorService service = Executors.newFixedThreadPool(2);
try 
    // do things with the executor service
 finally 
    service.shutdown();

如果你想阻塞直到所有任务完成,你可以使用awaitTermination,或者调用shutdown然后循环直到isTerminated返回true。如果您想尝试立即终止任何正在运行或排队的任务,您可以使用shutdownNow

【讨论】:

如果任务是可运行的,那么最好使用service.awaitTermination(..) @onkarruikar 没必要。 main 方法不会再做任何依赖于已完成任务的工作,此外,代码已经在它创建的期货上调用 get()。但是如果由于某种原因你需要它阻塞直到任务完成,你可以使用 awaitTermination。 @DavidConrad 因为使用awaitTermination 没有缺点,我建议将其作为一个好习惯来教。在你的好答案中至少值得一提。 而且,顺便说一句,如果Project Loom 成功,则此代码会变得更简单,因为ExecutorService 将是AutoCloseable,用于try-with-resources 语法。基于早期访问 Java 18 的实验性构建现在可用。 @BasilBourque 我会提到它,但我不明白这是一个好习惯。如果您不想在所有任务完成之前阻塞,则没有理由这样做。我想知道这里是否存在一种误解,即shutdown 会以某种方式杀死正在运行的任务,或者阻止排队的任务执行?它没有。

以上是关于使用 Callable 和 Future 时程序不会退出的主要内容,如果未能解决你的问题,请参考以下文章

Java程序员必须掌握的线程知识-Callable和Future

Java程序员必须掌握的线程知识-Callable和Future

Java程序员必须掌握的线程知识-Callable和Future

Future和Callable的使用

Callable/Future

java future函数的作用