在Servlet destroy()方法中调用System.exit()

时间:2009-02-13 14:23:16

标签: java tomcat servlets windows-services

这是对我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的更高版本)无法正常关闭。

4 个答案:

答案 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()”方法设为静态,这样所有线程都会通过一次关闭调用来获取信号。好主意还是没有?