在Servlet的destroy()方法中调用System.exit()
问题内容:
这是我先前提出的问题的后续措施。
Tomcat 5.0.28有一个错误,即容器在关闭时未调用Servlet的destroy()方法。这已在Tomcat
5.0.30中修复,但是如果Servlet的destroy()方法具有System.exit(),则将导致Tomcat
Windows服务抛出错误1053,并拒绝正常关闭(有关更多详细信息,请参见上面的链接)这个错误)
任何人都不知道是否:
-
在Servlet的destroy()方法中调用System.exit()以强制杀死任何非守护进程线程是一个好主意吗?
-
如果Servlet的destroy()方法中存在System.exit(),为什么Tomcat 5.0.30及更高版本(包括Tomcat 6.xx的更高版本)无法正确关闭。
问题答案:
您在问两个问题:
问题1:在Servlet的destroy()方法中调用System.exit()以强行杀死任何非守护进程线程是一个好主意吗?
在任何与Servlet相关的方法中调用System.exit()总是100%错误。您的代码不是在JVM中运行的唯一代码- 即使您是唯一在运行
的servlet(该servlet容器具有在JVM真正退出时也需要清除的资源)。
处理这种情况的正确方法是在destroy()方法中清理线程。这意味着以一种可以使您以正确方式轻轻停止它们的方式启动它们。这是一个示例(其中MyThread是您的线程之一,并且扩展了ServletManagedThread):
public class MyServlet extends HttpServlet {
private List<ServletManagedThread> threads = new ArrayList<ServletManagedThread>();
// lots of irrelevant stuff left out for brevity
public void init() {
ServletManagedThread t = new MyThread();
threads.add(t);
t.start();
}
public void destroy() {
for(ServletManagedThread thread : threads) {
thread.stopExecuting();
}
}
}
public abstract class ServletManagedThread extends Thread {
private boolean keepGoing = true;
protected abstract void doSomeStuff();
protected abstract void probablySleepForABit();
protected abstract void cleanup();
public void stopExecuting() {
keepRunning = false;
}
public void run() {
while(keepGoing) {
doSomeStuff();
probablySleepForABit();
}
this.cleanup();
}
}
还值得注意的是,那里有线程/并发库可以帮助您解决问题-
但是,如果您确实有少数几个线程是在Servlet初始化时启动的,并且应该一直运行到Servlet被销毁,这可能就是您所需要的。
问题2:如果Servlet的destroy()方法中存在System.exit(),为什么Tomcat 5.0.30及更高版本(包括Tomcat
6.xx的更高版本)无法正确关闭?
没有更多的分析,很难确定。 Microsoft说
Windows要求关闭服务但请求超时时,会发生错误1053。那将使它看起来像是在Tomcat内部发生了某种事情,使它陷入了非常糟糕的状态。我当然怀疑您致电System.exit(
)可能是罪魁祸首。Tomcat(特别是Catalina)确实在VM中注册了一个关闭钩子(see org.apache.catalina.startup.Catalina.start()
,至少在5.0.30中)。调用时,JVM将调用该关闭挂钩System.exit()
。关闭挂钩代表正在运行的服务,因此可能需要每个服务执行大量工作。
如果关闭钩子(triggered by your System.exit()
)无法执行(它们死锁或类似的问题),则在给出该Runtime.exit(int)
方法的文档(从中调用System.exit()
)的情况下,很容易理解为什么会发生错误1053
:
如果在虚拟机开始其关闭序列后调用此方法,则如果正在运行关闭挂钩,则此方法将无限期阻塞。如果已经运行了关闭挂钩,并且启用了退出完成功能,则如果状态为非零,则此方法将使用给定的状态代码来暂停虚拟机;否则,此方法将暂停虚拟机。否则,它将无限期地阻塞。
此“不确定阻止”行为肯定会导致错误1053。
如果您想要比此更完整的答案,则可以下载源代码并自行调试。
但是,我愿意打赌,如果您正确地处理了线程管理问题(如上所述),您的问题将会消失。
简而言之,将System.exit()调用留给Tomcat-这不是您的工作。