我们有一个服务方法getdatapar列(),目前可能被许多客户端调用,我们使用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();
}
}
通常在应用程序退出时关闭ExecutorService -大多数应用程序都有某种生命周期和关闭顺序。如果您希望应用程序在主线程完成它必须做的事情时退出,那么您希望在它的工作完成时关闭它(这意味着有一个定义良好的工作完成点,并且您可以告诉它何时完成)。
服务器端框架通常具有生命周期方法,您可以钩入这些方法来检测代码何时关闭并干净地退出。或者你可以使用虚拟机关机钩子(也许是延迟关机,直到所有当前排队的作业完成),这样无论什么代码导致程序退出,你的清理代码将运行。
一般来说,如果框架没有提供出口点,那么创建一个定义良好的出口点是一个好主意——它可以让你有一个应用程序可以被干净地卸载(也许,用更新的配置重新加载),而不需要关闭VM——我已经使用了这个技巧,能够让服务器应用程序重新配置自己,并在响应Unix信号时零停机重新加载。
所以,你的问题的简单答案是"当它不再被使用时"。在几乎任何应用程序中,都有一个点是明确无误的。
顺便说一句,为了与其他响应器之一相矛盾,ExecutorService 可以使用守护线程——您可以提供一个ThreadFactory,在启动线程之前按照您的意愿配置线程。但是我鼓励您不要使用守护线程,并在一个定义良好的点显式地关闭线程池——这不是典型的做法,但这既意味着您的代码有可能被干净地关闭,也将鼓励您考虑生命周期,这可能会导致更好的代码。Java中有两种类型的线程(当然取决于您如何看待它们)。"用户"线程和"守护进程"线程。您的应用程序在下列情况之一结束:
- 呼叫
System.exit()
- 您的应用程序中没有
User
线程。
注意你的main
函数是由JVM在一个'User'线程上执行的,这意味着只要你还没有完成你的main
函数。大多数多线程应用程序只会运行main函数来启动所需的所有线程。
Daemon线程背后的思想是,你可以做一些事情(定期),但如果所有其他任务都完成了,它不会阻止应用程序退出。
默认情况下,新线程是'Non Daemon'线程,由ExecutorService
创建的线程也是如此。如果你想改变这一点,你必须创建自己的ThreadFactory
。ThreadFactory
允许您手动为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());
请注意,这也是给线程自定义名称的方式,这是非常有用的,因为许多日志框架记录线程名称(调试器显示它)。
如果你要这样做,在你的应用程序中,它会立即退出,因为你只创建'Daemon'线程,所以你必须保持另一个线程存活(这可以由另一个框架隐式地完成,例如,如果你有GUI)。
另一种方法是手动调用System.exit()
。通常不建议在代码中调用System.exit()
。主要是因为它不允许良好的重构、重用、测试,而且许多退出点使您的应用程序不可预测。为了避免这些问题,您可以创建一个回调函数来处理作业完成事件。在这个应用程序中,您调用System.exit()
,您的测试代码或其他应用程序可以做其他事情。
在应用程序环境中,必须调用shutdown来确保ExecutorService
启动的线程必须停止,并且不能再接受任何新的任务。否则JVM不会退出。
在Service的情况下,你应该在停止Service执行之前调用shutdown
。例如,在web-app的情况下,ServletContextListener
的contextDestroyed
方法可以用来调用ExecutorService
的shutdown
方法。这将确保任何现有的任务必须在突然终止申请之前完成,但不会接受新任务进行处理。
然而,我发现除非我调用executorService.shutdown();应用程序永远不会退出,
我认为我们不需要调用executorService.shutdown();让应用程序保持活力,对吗?
是的。除非调用executorService.shutdown()
每个应用程序都应该有一个退出点。您可以在退出点调用shutdown。
如果你没有任何已知的退出点,ShutdownHook是解决问题的一种方法。但是你应该知道
在极少数情况下,虚拟机可能会中止,即停止运行而不彻底关闭
对于你所引用的例子,你可以用多种方式解决它(invokeAll, Future.get(), CountDownLatch
等)。请看相关的SE问题
ExecutorService,如何等待所有任务完成