当executorService.shutdown();应该叫

时间:2016-08-30 05:41:24

标签: java multithreading threadpool

我们有一个服务方法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();
    }
}

4 个答案:

答案 0 :(得分:5)

通常,在应用程序退出时关闭ExecutorService - 大多数应用程序都有某种生命周期和关闭顺序。如果您希望应用程序在主线程完成任何必须执行的操作时退出,那么您希望在其工作完成时将其关闭(这意味着有一个明确定义的点,它的工作已完成,并且您可以告诉它何时是)。

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

一般情况下,如果没有框架提供的退出点,最好创建一个明确定义的退出点 - 它允许您拥有一个可以干净地卸载的应用程序(并且可能重新加载更新的配置)虚拟机必然会完全关闭 - 我已经使用了这个技巧,可以让服务器应用程序自行重新配置,并在没有停机时重新加载以响应Unix信号。

所以,对你的问题的简单回答是“当它不再被使用时”。几乎在任何应用程序中都有一个明确无误的点。

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

答案 1 :(得分:1)

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

如果是Service,您应该在停止服务执行之前调用shutdown。对于例如对于网络应用,contextDestroyed ServletContextListener方法可用于调用shutdown的{​​{1}}方法。这将确保在突然终止申请之前必须完成任何现有任务,但不会接受任何新任务进行处理。

答案 2 :(得分:1)

Java中有两种线程(当然取决于你如何看待它们)。 &#39;用户&#39;线程和&#39;守护进程&#39;线程。您的应用程序在以下某种情况下结束:

  1. 您致电System.exit()
  2. 您的申请中没有User个帖子。这解释为here
  3. 请注意,您的main功能由JVM在用户&#39;上执行。线程,意味着只要您尚未完成main功能。大多数多线程应用程序只运行main函数来启动所需的所有线程。

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

    默认情况下,新主题是&#39;非守护进程&#39;线程,ExecutorService提供的线程也是如此。如果您想更改此设置,则必须创建自己的ThreadFactoryThreadFactory允许您手动为ExecutorService创建线程,当ExecutorService需要新线程时,将调用它。这是一个创建“守护进程”的例子。线程:

    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都会记录线程名称(并且调试器会显示它)。

    如果你这样做,在你的应用程序中,它会立即退出,因为你只是创建了守护进程&#39;线程,所以你必须保持另一个线程活着(这可以由另一个框架隐式完成,例如,如果你有GUI)。

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

答案 3 :(得分:1)

  

但是我发现除非我调用executorService.shutdown();应用程序永远不会退出,

     

我认为我们不需要调用executorService.shutdown();保持应用程序活着,对吗?

是。除非您致电executorService.shutdown()

,否则申请仍然有效

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

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

  

在极少数情况下,虚拟机可能会中止,即停止运行而不会干净地关闭

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

ExecutorService, how to wait for all tasks to finish