当 executorService.shutdown();应该叫

When the executorService.shutdown(); should be called

我们有一个服务方法GetDataParallel( ),目前可能被很多客户端调用,我们使用ExecutorService调用其中的MyCallable。但是我发现除非我调用 executorService.shutdown();应用程序永远不会退出,那么为什么应用程序不能退出,我们必须在应用程序退出之前手动关闭所有线程池线程?在服务环境中,我认为我们不需要调用 executorService.shutdown();让应用程序保持活力,对吗?

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MultiThreading {

    static ExecutorService executorService = Executors.newFixedThreadPool(100);
    private List<String> _BusinessUnits= new ArrayList<String>();
    public static void main(String[] args) throws Exception {

        MultiThreading  kl =new MultiThreading();
        kl.GetDataParallel();
        Thread.sleep(10000);
        System.out.println("111111111");
         //executorService.shutdown();

    }

    public   void GetDataParallel( ) throws Exception
    {
        _BusinessUnits.add("BU1");
        _BusinessUnits.add("BU2");
        _BusinessUnits.add("BU3");

        for(final String v : _BusinessUnits)
        {
            ExecutorServiceTest.executorService.submit( new MyCallable());
        }
    }
}

  class MyCallable implements Callable  {
    @Override
    public String call() throws Exception {
        Thread.sleep(1000);
        //return the thread name executing this callable task
        System.out.println(Thread.currentThread().getName());
        return Thread.currentThread().getName();
    }
}

在应用程序环境中,您必须调用 shutdown 以确保 ExecutorService 启动的线程必须停止并且它不应再接受任何新任务。否则JVM不会退出。

对于服务,您应该在停止服务执行之前调用 shutdown。例如对于网络应用程序,ServletContextListenercontextDestroyed 方法可用于调用 ExecutorServiceshutdown 方法。这将确保任何现有任务必须在应用程序突然终止之前完成,但不会接受任何新任务进行处理。

通常您会在应用程序退出时关闭 ExecutorService - 大多数应用程序都有某种生命周期和关闭顺序。如果你希望你的应用程序在主线程完成它必须做的任何事情时退出,你想在它的工作完成时关闭它(这意味着它的工作完成有一个明确定义的点,你可以知道什么时候是)。

服务器端框架通常具有生命周期方法,您可以挂钩这些方法以检测代码何时关闭并干净退出。或者您可以使用 VM 关闭挂钩(可能会延迟关闭,直到所有当前排队的作业都完成),这样无论是什么代码导致程序退出,您的清理代码都将是 运行.

一般来说,如果框架没有提供明确定义的退出点,那么创建一个明确定义的退出点是个好主意 - 它可以让您拥有一个可以干净卸载的应用程序(也许可以使用更新的配置重新加载) ) VM 根本不需要关闭 - 我已经使用了这个技巧来让服务器应用程序重新配置自身并以零停机时间重新加载以响应 Unix 信号。

因此,您问题的简单答案是 "When it isn't going to be used anymore"。在几乎所有应用程序中,都有一个点是明确正确的。

顺便说一句,为了与其他响应者之一相矛盾,ExecutorService 可以 使用守护进程线程 - 您可以提供一个 ThreadFactory 来配置线程,但是在您启动它们之前。但我鼓励您不要使用守护线程并在明确定义的点显式关闭线程池——这不是典型的做法,但这都意味着您的代码有可能被干净地关闭,并且它将鼓励您考虑生命周期,这可能会带来更好的代码。

Java 中有两种线程(当然取决于您如何看待它们)。 'User' 个线程和 'Daemon' 个线程。您的申请以下列情况之一结束:

  1. 你打电话给System.exit()
  2. 您的应用程序中没有 User 个线程。这是解释 here.

请注意,您的 main 函数由 JVM 在 'User' 线程上执行,这意味着只要您尚未完成 main 函数。大多数多线程应用程序将 运行 main 函数仅用于启动所有需要的线程。

守护线程背后的想法是你可以(定期)做一些事情,但是如果所有其他任务都完成了,它不会阻止应用程序退出。

默认情况下,新线程是 'Non Daemon' 个线程,您的 ExecutorService 创建的线程也是如此。如果你想改变这个,你必须创建你自己的 ThreadFactoryThreadFactory 允许您为 ExecutorService 手动创建线程,当 ExecutorService 需要新线程时将调用它。这是创建 'Daemon' 个线程的示例:

public class DaemonThreadFactory implements ThreadFactory
{

    @Override
    public Thread newThread(final Runnable r)
    {
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
    }
}

然后可以通过创建执行程序服务来使用它:

ExecutorService service = Executors.newFixedThreadPool(100, new DaemonThreadFactory());

请注意,这也是给您的线程自定义名称的方法,这非常有用,因为许多 logframeworks 会记录线程名称(并且调试器会显示它)。

如果你这样做,在你的应用程序中,它会立即退出,因为你只创建 'Daemon' 个线程,所以你将不得不保持另一个线程处于活动状态(这可以隐式地完成另一个框架,例如如果你有 GUI)。

另一种方法是手动调用 System.exit()。通常不建议在您的代码中调用 System.exit()。主要是因为它不允许进行良好的重构、重用、测试和许多退出点,使您的应用程序不可预测。为了避免这些问题,您可以创建一个回调函数来处理作业完成事件。在您调用 System.exit() 的这个应用程序中,您的测试代码或其他应用程序可以执行其他操作。

However I found unless I called the executorService.shutdown(); the application never exit,

I think we don't need to call the executorService.shutdown(); to keep the application alive, right ?

是的。除非您调用 executorService.shutdown()

,否则应用程序仍处于活动状态

每个应用程序都应该有一个退出点。您可以在该退出点调用 shutdown。

如果您没有任何已知的退出点,ShutdownHook 是解决您问题的一种方法。但你应该知道

In rare circumstances the virtual machine may abort, that is, stop running without shutting down cleanly

对于您引用的示例,您可以通过多种方式解决它(invokeAll, Future.get(), CountDownLatch 等)。查看相关的 SE 问题。

ExecutorService, how to wait for all tasks to finish