我正在开发一个要部署到Tomcat上的Web应用程序。启动Tomcat时,我使用servlet(在web.xml中)来调用Java类:
<web-app>
<display-name>Consumer</display-name>
<servlet>
<servlet-name>start</servlet-name>
<servlet-class>com.test.sample.Consumer</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
</web-app>
我的Consumer.java订阅了AMQP服务器上的队列。我通过使用while (true)
循环来实现这一点,它在独立的Java程序中运行良好。它也适用于Web应用程序的上下文,但我永远不能停止我的Tomcat服务器(在我的NetBeans IDE中),我相信while循环是罪魁祸首。这是一些代码:
public class Consumer {
public Consumer()
consume();
}
private void consume()
...
while (true) {
// Await incoming messages from queue
// Process message
}
}
}
有没有更好的方法来解决这个问题?或者发出停止信号以阻止循环?
谢谢!
更新为使用ServletContextListener:
public final class ApplicationListener implements ServletContextListener {
private ScheduledExecutorService scheduler;
public ApplicationListener() {
}
@Override
public void contextDestroyed(ServletContextEvent event) {
System.out.println("***** Stopping Consumer *****");
scheduler.shutdownNow();
}
@Override
public void contextInitialized(ServletContextEvent event) {
System.out.println("***** Starting Consumer *****");
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new ScheduledConsumer(), 0, 15000, TimeUnit.MILLISECONDS);
}
public class ScheduledConsumer implements Runnable {
@Override
public void run() {
Consumer k = new Consumer();
k.consumeOnce();
}
}
}
答案 0 :(得分:3)
我有一些建议,但它们要求您稍微修改一下您的体系结构,以便更好地使用您的容器环境。
Servlet容器支持可以获取各种事件通知的“侦听器”。具体来说,其中一个是ServletContextListener
,当上下文(也称为webapp)投入使用(通过contextInitialized
方法)并且当它停止服务时(通过contextDestroyed
方法)。
我的建议是做以下事情:
Consumer
类的构造函数,使其不会自动调用consume()
;相反,添加一个公共方法,如consumeOnce
,并且不要在所有级别使用循环ServletContextListener
作为成员的Consumer
和Thread
引用以及volatile boolean stop
标记;在contextInitialized
中它应该创建一个新的Consumer
对象,然后启动一个新的(守护进程)线程:
Consumer.consumeOnce
Thread.sleep
一段适当的时间ServletContextListener
的{{1}}方法将停止标记设置为contextDestroyed
,并在正在运行的主题上调用true
。我确定我错过了一些确切的细节,但这是一般的想法。当Tomcat关闭时,您的代码将收到关闭通知,您可以干净地终止自己的循环线程。您可能需要为Thread.interrupt
提供一种方法来中止尝试消耗其消耗的任何内容(例如,如果在获得{{1}时它没有中止,则停止等待从空队列中提取对象)信号。 (例如,如果您使用Consumer
等待监视器通知,那么您将要更改它,以便它使用等待超时,以便您不会永远阻止。
答案 1 :(得分:-2)
您必须将包含循环的代码放在不同的线程中,然后从您的使用者处启动该线程。
private void consume() {
Thread x = new Thread(new Runnable() {
@Override
public void run() {
while(true) {
....
}
});
x.start();
}