在所有线程完成之前从 java 方法返回

Posted

技术标签:

【中文标题】在所有线程完成之前从 java 方法返回【英文标题】:Returning from java method before all threads are done 【发布时间】:2019-04-01 21:29:18 【问题描述】:

是否可以在所有线程完成之前从启动线程的 java 方法返回?

这是我的代码:

import java.util.concurrent.*;

public class ExecutorSample 
    public static void main(String[] args) 
        ExecutorService service = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) 
            service.execute(() -> 
                while (true) 
                
            );
        
        return;
    

由于某种我不明白的原因,这不会返回。为什么工作线程卡住会影响主线程的返回?

【问题讨论】:

你怎么知道方法没有返回? @khelwood 我测试过,它卡在 main 上。 @ScaryWombat shutdownNow 也不起作用,因为它只是最好的尝试,实际上并没有关闭工作线程 @Jessica 你必须向我们展示你的测试结果,这可能是为什么有人不赞成你。此外,您应该在代码示例中显示测试。 将代码放在另一个方法中,从main调用它,在另一个方法返回后在main中打印一些东西来测试其他方法是否返回。 【参考方案1】:

您的main 方法正在返回,但问题是您的JVM 没有退出。您可以通过将您的 main 方法重命名为其他名称来测试这一点,例如 doMain(),然后将 main 写为:

public static void main(String[] args) 
    doMain();
    System.out.printf("here");

你会看到你确实得到了输出,但程序并没有结束。

发生的事情是Executors.newCachedThreadPool 使用default thread factory,它创建了非守护线程。当您的main 方法返回时,JVM 将等待所有非守护线程完成(这就是它们与守护线程的区别——JVM 不会等待这些线程)。由于你所有的 Runnables 永远运行,非守护线程永远不会结束,JVM 永远不会退出。

你能做什么?一些选项:

使用重载newCachedThreadPool(ThreadFactory),提供创建守护线程的ThreadFactory 在您的 ExecutorService 上使用 shutdownNow()。这将在每个正在运行的线程上发送一个中断。您仍然需要在每个线程上检查它,方法是调用抛出 InterruptedException 的方法或显式调用 Thread.currentThread().isInterrupted()

对于shutdownNow 方法,请注意仅中断一个线程不足以停止它——在该线程上运行的代码必须通过检查其中断状态(使用一个这两种方法中的一种)并适当地退出。例如,您可以将while(true) 替换为while (!Thread.currentThread().isInterrupted())

【讨论】:

@yshavit 从您的回答看来,提供创建守护线程的线程工厂似乎是更好的方法。这种方法有什么缺点吗? @Jessica:守护线程的缺点是,如果最后一个非守护线程退出,它们就会消失,没有机会进行清理。不要将守护线程用于您关心以受控方式完成的任何事情。 @NathanHughes Cool 有没有 Java 原生方式来构建这个工厂?我尝试了可以​​工作的番石榴 ThreadFactoryBuilder,但原生 java 方式会更好 很高兴为您提供帮助,杰西卡!我不知道创建自定义线程工厂比番石榴更好的方法,或者只是编写自己的。创建 Thread 并不难,希望 javadocs 是不言自明的。 (另外,感谢@NathanHughes 修正了这个错字!)

以上是关于在所有线程完成之前从 java 方法返回的主要内容,如果未能解决你的问题,请参考以下文章

java多线程-CountDownLatch

Java Concurrency - 浅析 CountDownLatch 的用法

面试官:如何让主线程等待所有的子线程结束之后再执行?我懵了

Java线程池,isShutDownisTerminated的作用与区别

jmeter 正则获取多个返回token至本地文件,并跨线程组调用

在异步操作完成之前从方法返回的坏习惯?