这是对我earlier question的跟进。
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的更高版本)无法正常关闭。
答案 0 :(得分:16)
在Servlet的destroy()方法中调用System.exit()强制终止任何非守护进程线程是个好主意吗?
这绝对不是一个好主意 - 这是一个可怕的想法。当servlet停止服务时调用destroy()
方法,这可能由于多种原因而发生:servlet / webapp已停止,webapp正在取消部署,webapp正在重新启动等。
System.exit()
关闭整个JVM !为什么要仅仅因为正在卸载一个servlet而强制关闭整个服务器?
如果Servlet的destroy()方法中存在System.exit(),为什么Tomcat 5.0.30和(包括Tomcat 6.x.x的更高版本)无法正常关闭。
可能是为了防止这样的危险行为。
您不应编写代码,假定您的代码/应用程序是服务器上运行的唯一内容。
答案 1 :(得分:12)
你问了两个问题:
问题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 says当Windows要求服务关闭时发生错误1053,但请求超时。这将使Tomcat内部发生的事情变得非常糟糕。我当然会怀疑你对System.exit(
)的打电话可能是罪魁祸首。 Tomcat(特别是Catalina)确实向VM注册了一个关闭钩子(see org.apache.catalina.startup.Catalina.start()
,至少在5.0.30中)。当您调用System.exit()
时,JVM会调用该关闭钩子。关闭挂钩委托给正在运行的服务,因此可能需要每个服务执行大量工作。
如果关闭钩子(triggered by your System.exit()
)无法执行(它们死锁或类似的东西),那么在给出Runtime.exit(int)
方法的文档的情况下,很容易理解为什么会出现错误1053 (从System.exit()
调用):
如果在此之后调用此方法 虚拟机已经开始关机 然后,如果关闭钩子是序列 正在运行此方法将阻止 无限期。如果关机挂钩有 已经运行和退出 然后启用了终结 此方法暂停虚拟机 如果是,给定状态代码 状态不为零;否则,它 无限期地阻止。
这种“无限期阻塞”行为肯定会导致错误1053。
如果你想要一个比这更完整的答案,你可以download the source并自己调试。
但是,我愿意打赌,如果你正确处理线程管理问题(如上所述),你的问题就会消失。
简而言之,将System.exit()调用留给Tomcat - 这不是你的工作。
答案 2 :(得分:3)
在一个内部调用System.exit() Servlet的destroy()方法为 强制杀死任何非守护进程线程 是一个好主意?
不是个好主意。您将强制终止所有线程,这些线程可能包含当前正在关闭系统的Tomcat的一部分。这将导致Tomcat无法正常关闭。这也可以防止关机处理程序运行,从而导致各种问题。
为什么Tomcat 5.0.30和(稍后 版本包括Tomcat 6.x.x)失败 如果有的话,正确关机 destroy()方法中的System.exit() Servlet。
代码的 lot 在Servlet目标之后执行。 Context销毁以及其他所有其他servlet的监听器。其他应用。 Tomcat自己。通过调用System.exit,可以防止所有这些运行。
更好的问题是这些非守护程序线程是什么,它们为什么运行,以及谁启动它们?
答案 3 :(得分:0)
在编写像Jared这样的线程关闭代码时,我通常会将“keepGoing”成员和“stopExecuting()”方法设为静态,这样所有线程都会通过一次关闭调用来获取信号。好主意还是没有?