【问题标题】:Correct way to create task threads in tomcat at startup [duplicate]在启动时在tomcat中创建任务线程的正确方法[重复]
【发布时间】:2014-11-23 22:19:05
【问题描述】:

阅读了很多不同的东西并且对其中一些东西的正确术语并不完全熟悉,我想知道在 tomcat 启动时创建线程的正确方法是什么 特定目的。我用的是linux系统。

我不是想从 servlet 创建线程,而且我很清楚为什么这是不好的。当tomcat启动时,我想要两个具有不同目的的不同线程来启动。第一个线程每隔 30 分钟运行一次,以审核后端数据。第二个线程负责发送电子邮件。当一个请求运行一个必须发送电子邮件的 servlet 时,我不想让 servlet 等待电子邮件发送,而是想向这个电子邮件线程发送一个请求并继续前进。因此,一个定期运行,一个按需运行。我从不需要增加执行这些任务的线程数,我只需要这两个,只要 webapp 正在运行,它就会一直运行,如果 webapp 必须停止,我并不关心优雅关机。

我知道我可以采取以下几种方法:

  1. 创建一个在tomcat 外部 运行的进程,并定义一种从我的servlet 与该进程通信的方式。如果可能的话,我想避免这种情况,因为我希望这两个线程首先直接与 webapp 的启动和关闭相关联,并且让它们能够访问ServletContext 中的数据,并且不必通过通信渠道对数据进行 ser/des。

  2. 在我的一个 servlet 的 init 方法中生成两个线程。这看起来很脏而且很老套,但它肯定会完成工作。

  3. 创建一个扩展ThreadPoolExecutor 或其他ExexutorServiceServletContextListener。这看起来很有吸引力且正确,我想我可以做一个由 2 个线程组成的固定线程池,因为我不再需要了。但这是推荐的方法来做我正在尝试的事情吗?我知道ExecutorService 更适合即时执行Runnables,而不必失去创建线程的开销。我不知道这是否完全正确,但如果是,我就不会使用执行程序来达到预期目的。

也许还有更多我不知道的方法,或者实现上述方法之一的正确方法。

【问题讨论】:

  • 首先,这与Tomcat无关,请编辑问题。您的问题是如何在 web 应用程序 中启动服务线程。其次,您的第三种方法几乎是正确的-但不要extends,这肯定是错误的。此外,考虑两个听众,因为它们似乎是独立的关注点,将它们组合在一起也是一种技巧。确保您理解并使用ScheduledExecutorService,并确保您正确关闭了执行程序。
  • 听起来你真正想要的是两个Singletons。
  • 关于这与tomcat无关,我特别提到tomcat是因为我会尝试在WebSphere中使用WorkManager做类似的事情,我会将Work传递给它。我不相信 Tomcat 本身具有这种能力(尽管我认为 JBOSS 可能),所以我特别对像 Tomcat 这样的轻量级 servlet 处理器的选项感兴趣。

标签: java tomcat servlets threadpool


【解决方案1】:

请参考以下问答:tomcat 6 thread pool for asynchronous processing

此外,您可能需要 Executors.newScheduledThreadPool 来创建能够执行重复任务的 ScheduledExecutorService 实例,而不是 Executors.newFixedThreadPool

希望这会有所帮助...

【讨论】:

  • Executors.newFixedThreadPool 用于单个按需线程和Executors.newScheduledThreadPool 用于我的定期任务是否有意义?只为两个线程选择一个(例如,fixedThreadPool)并让我的定期任务适应它会更有意义吗? (如使用Thread.sleep(30 minutes) 来确保周期性?)
  • @MichaelPlautz 不要为这两个任务使用同一个池,因为这两个任务完全不相关(从您描述它们的方式来看)。并且永远不要使用Thread.sleep - 如果你正在考虑使用它,那么你做错了什么。
  • @MichaelPlautz 我完全同意Boris the Spider - 从不使用 Thread.sleep 或类似方法,因为它是 低级方式 在 Java 中使用多线程,仅在需要使用 ExecutorService 和/或无法实现的非常具体的多线程逻辑时才应使用 ScheduledExecutorService,这被认为是在 Java 中使用多线程的高级(因此不易出错)方式。跨度>
  • @MichaelPlautz 关于使用哪个 ExecutorServices:我建议您通过调用 @ 创建 一个实例 ScheduledExecutorService 987654331@。现在,您显然需要池中至少 2 个线程用于 2 个周期性任务 + 一些线程用于可能在 webapp 生命周期内发生的其他(非周期性)异步任务。 Java 中的线程并不昂贵,因此池中有 10 个线程不会显着影响内存/性能,但应该足以满足基本需求。希望这会有所帮助...
【解决方案2】:

我不会继续实现线程池本身,而是关于你的问题:

启动时在tomcat中创建任务线程的正确方法

正如其他人所说,您的第三种方法几乎是正确的但是这取决于您的服务结构。

我给你举个例子然后解释一下:

public class YourServletContextListener implements ServletContextListener{     
    @Override
    public void contextDestroyed(ServletContextEvent sce) {

    }         
    @Override
    public void contextInitialized(ServletContextEvent sce) {

    }
}

来自文档:

contextInitialized:

所有 ServletContextListener 都会收到上下文初始化通知 在 Web 应用程序中的任何过滤器或 servlet 初始化之前

如果我正确理解您的方法,那么我会问您:您希望如何启动服务或向尚未初始化的 servlet 发送任何请求?

如果您想启动的服务不直接与 Web 应用程序通信/不需要任何 servlet 或过滤器或任何数据,这将起作用。之后,启动容器后,它们就可以相互通信了。

正如我之前所说,最好使用哪种方式取决于服务结构/逻辑。

另一种方法可能是使用过滤器:

void init(FilterConfig filterConfig) throws ServletException

由 Web 容器调用以向过滤器指示它正在被 投入使用。 servlet容器调用init方法 在实例化过滤器后恰好一次。

void destroy()

由 Web 容器调用以向过滤器指示它正在被 停止服务。此方法仅在所有线程中调用一次 在过滤器的 doFilter 方法内已退出或超时后 期已过。 web容器调用该方法后,会 不要在此过滤器实例上再次调用 doFilter 方法。

此方法使过滤器有机会清理任何资源 被持有的(例如,内存、文件句柄、线程)和 确保任何持久状态与过滤器的同步 内存中的当前状态。

但过滤器不是为此类方法设计的!

如果你想拦截 HTTP 请求,请使用过滤器 特定的 URL 模式,因为您要检查/修改 HTTP 请求/响应。如果需要,请使用 ServletContextListener 在 webapp 的启动和/或关闭时拦截。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-30
    • 2017-03-02
    • 2012-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多