1

我正在开发一个要部署到 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 程序中运行良好。Itt 也适用于 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();
        }
    }
}
4

2 回答 2

3

我有一些建议,但它们要求您稍微修改您的架构,以便更好地使用您的容器环境。

Servlet 容器支持可以获取各种事件通知的“侦听器”。具体来说,其中之一是ServletContextListener当上下文(又名 webapp)被启用(通过contextInitialized方法)和停止服务(通过contextDestroyed方法)时得到通知。

我的建议是执行以下操作:

  1. 更改Consumer类的构造函数,使其不会自动调用consume(); 相反,添加一个公共方法,consumeOnce并且根本不使用该级别的循环
  2. 写一个ServletContextListener有一个Consumer和一个Thread引用作为成员以及一个volatile boolean stop标志的a;在contextInitialized它应该创建一个新Consumer对象,然后启动一个新的(守护进程)线程:
    • 来电Consumer.consumeOnce
    • 要求Thread.sleep适当的时间
    • 循环前两个步骤,直到停止标志为真
  3. 让您ServletContextListenercontextDestroyed方法将停止标志设置为true并调用Thread.interrupt正在运行的线程。

我确定我遗漏了一些确切的细节,但这是一般的想法。当 Tomcat 关闭时,您的代码将收到关闭通知,您可以干净地终止自己的循环线程。如果它在收到信号时没有中止,您可能需要提供一种方法Consumer来中止尝试使用它所消耗的任何东西(例如,停止等待从空队列中拉出对象) 。Thread.interrupt(例如,如果您使用 anObject.wait()来等待监视器通知,那么您需要更改它,以便它使用带有超时的等待,这样您就不会永远阻塞)。

于 2013-01-05T14:45:16.540 回答
-2

您必须将带有循环的代码放在不同的线程中,并从您的使用者启动线程。

private void consume() {
  Thread x = new Thread(new Runnable() {
    @Override
    public void run() {
      while(true) {
      ....
      }
   });
   x.start();
}
于 2013-01-04T20:42:05.480 回答