一尘不染

无法停止使用while(true)循环运行Web应用程序的tomcat服务器

tomcat

我正在开发一个将部署到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();
        }
    }
}

阅读 578

收藏
2020-06-16

共1个答案

一尘不染

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

Servlet容器支持可获取各种事件通知的“侦听器”。具体来说,其中之一是ServletContextListener当上下文(通过Web
contextInitialized方法)投入使用(通过方法)和何时将其退出服务(通过contextDestroyed方法)时得到通知的通知。

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

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

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

2020-06-16