在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 threads = new ArrayList(); // 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的更高版本)无法正常关闭?

没有更多的分析,很难确定。 微软表示 ,当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() )无法执行(它们死锁或类似的东西),那么很容易理解错误1053发生的原因,给出了Runtime.exit(int)的文档方法(从System.exit()调用):

如果在虚拟机开始其关闭序列后调用此方法,那么如果正在运行关闭挂钩,则此方法将无限期地阻塞。 如果已经运行了关闭挂钩并且已启用了退出终结,则如果状态为非零,则此方法将使用给定的状态代码暂停虚拟机; 否则,它无限期地阻止。

这种“无限期阻塞”行为肯定会导致错误1053。

如果您想要比这更完整的答案,您可以下载源代码并自行调试。

但是,我愿意打赌,如果你正确处理线程管理问题(如上所述),你的问题就会消失。

简而言之,将System.exit()调用留给Tomcat – 这不是你的工作。

在Servlet的destroy()方法中调用System.exit()来强制终止任何非守护进程线程是个好主意?

这绝对不是一个好主意 – 这是一个可怕的想法。 当servlet退出服务时会调用destroy()方法,这可能由于多种原因而发生:servlet / webapp已停止,webapp正在取消部署,webapp正在重新启动等。

System.exit()关闭整个JVM ! 为什么要仅仅因为正在卸载一个servlet而强行关闭整个服务器?

如果Servlet的destroy()方法中存在System.exit(),为什么Tomcat 5.0.30和(包括Tomcat 6.xx的更高版本)无法正常关闭。

可能是为了防止这样的危险行为。

您不应该编写代码,假定您的代码/应用程序是服务器上运行的唯一内容。

在Servlet的destroy()方法中调用System.exit()来强制终止任何非守护进程线程是个好主意?

不是个好主意。 您将强制终止所有线程,这些线程可能包括当前正在关闭系统的Tomcat的一部分。 这将导致Tomcat无法正常关闭。 这也可以防止关机处理程序运行,从而导致各种问题。

如果Servlet的destroy()方法中存在System.exit(),为什么Tomcat 5.0.30和(包括Tomcat 6.xx的更高版本)无法正常关闭。

许多代码在Servlet目标之后执行。 Context销毁以及其他所有其他监听器…其他servlet。 其他应用。 Tomcat自己。 通过调用System.exit,可以防止所有这些运行。

一个更好的问题是这些非守护程序线程是什么,它们为什么运行,以及谁启动它们?

在编写像Jared这样的线程关闭代码时,我通常会将“keepGoing”成员和“stopExecuting()”方法设置为静态,以便所有线程通过一次关闭调用获得信号。 好主意还是不?