一个Tomcat servlet应用的后台线程

63

我对Tomcat不太熟悉,我认为它基本上是一个cgi服务器,可以在调用之间保存JVM(Java虚拟机) - 我知道它可以做更多的事情。

我正在寻找一种方法,在Tomcat服务器启动时启动后台线程,该线程会定期更新服务器上下文(在我的特定案例中,这是一个侦听来自其他服务的心跳并更新可用性信息的线程,但人们可以想象出各种用途)。

是否有标准方法来实现这一点?包括启动和更新/查询上下文。

如果能提供相关文档和/或代码示例,将不胜感激。


值得注意的是,如果有比上下文初始化时更方便的地方,通常您可以随时启动线程。例如,在特定 servlet 的 init 方法中,或者在特定类第一次初始化时等等。 - Neil Coffey
5个回答

83

如果你想在部署WAR文件时启动一个线程,你可以在web.xml中定义一个上下文监听器:

<web-app>
    <listener>
       <listener-class>com.mypackage.MyServletContextListener</listener-class>
    </listener>
</web-app>

然后实现那个类的代码如下:

public class MyServletContextListener implements ServletContextListener {

    private MyThreadClass myThread = null;

    public void contextInitialized(ServletContextEvent sce) {
        if ((myThread == null) || (!myThread.isAlive())) {
            myThread = new MyThreadClass();
            myThread.start();
        }
    }

    public void contextDestroyed(ServletContextEvent sce){
        try {
            myThread.doShutdown();
            myThread.interrupt();
        } catch (Exception ex) {
        }
    }
}

啊!谢谢,这很简单明了。 我说"ServletContext"是我需要修改的内容,以便让这个线程传递信息给我的servlet,这样它就可以使用我的心跳监听器收集的状态了,这样说对吗? - SquareCog
1
是的,在我的回答中漏掉了这一部分。 :) ServletConext可以从ServletContextEvent中获取,该对象可以传递到您的Thread对象中,从而可以获取/设置对所有线程可用的属性。 - Chris Thornhill
此时,如何访问"MyServletContextListener"以查询线程? - Daniele Orlando

12

我正在寻找一种方法,在Tomcat服务器启动时启动一个后台线程

我认为您正在寻找一种方法,在Tomcat启动您的Web应用程序时启动后台线程。

这可以通过使用ServletContextListener来实现。该监听器在web.xml中注册,并在您的应用程序启动或停止时调用。然后,您可以使用普通的Java方式创建线程(或ExecutionService)来创建(和稍后停止)您的线程。


5
在您的web.xml文件中将<load-on-startup>1</load-on-startup>放置在块中,可以使您的servlet的init()在Tomcat启动时立即发生,而不必等待第一个请求到达。 如果您想从init()生成后台线程,则此功能非常有用。

这对我来说很管用,与其他解决方案相比如此简单,但却未被充分评价,非常适合启动一些后台应用程序或守护进程。谢谢! - Arturas M

3

我想对Chris提供的非常详细的答案做出一点小修改; 我会通过myThread.setDaemon(true);myThread设置为守护线程,这将使得当您有其他非守护线程需要您的后台线程时,该线程保持活动状态。当所有这些线程完成时,JVM将停止您的守护线程,并且您不需要在contextDestroyed中自己处理它。

但这只是我的个人意见。


14
除非你仍然需要在contextDestroyed中处理它。守护进程会在最后一个非守护线程终止时终止,而不是在servlet上下文终止时终止。如果您部署WAR文件以替换当前正在运行的文件,则来自旧servlet上下文的守护线程将继续运行。这反过来会保留该上下文的ClassLoader,其中包含PermGen内存泄漏,这是不好的。 - David Leppik

2

如果您使用基于Spring框架的方式,您可以在beans.xml中指定要初始化的类/线程。因此,当Tomcat启动时,beans.xml将初始化其中提到的所有类。如果需要,您还可以传递构造函数参数。以下是示例。

beans.xml

<bean id="monitoringSvc" class="com.mypackage.MonitoringService">
    <constructor-arg value="60"></constructor-arg>
</bean>

MonitoringService.java

public class MonitoringService{

     private MyThread myThread;

     public MonitoringService(int seconds){
          myThread = new MyThread(seconds);
          myThread.start();
     }
}    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接