使用计时器在 JSF 托管 bean 中为计划任务生成线程 [英] Spawning threads in a JSF managed bean for scheduled tasks using a timer

查看:17
本文介绍了使用计时器在 JSF 托管 bean 中为计划任务生成线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道在应用程序作用域 bean 中使用 Timer 是否可以.

I would like to know if it's ok to use Timer inside application scoped beans.

例如,假设我想创建一个计时器任务,每天一次向每个注册会员发送一堆电子邮件.我正在尝试使用尽可能多的 JSF,我想知道这是否可以接受(我知道这听起来有点奇怪).

Example, lets say that I want to create a timer task that sends out a bunch of emails to every registered member one time per day. I'm trying to use as much JSF as possible and I would like to know if this is acceptable (it sounders a bit weird, I know).

到目前为止,我已经在 ServletContextListener 中使用了上述所有内容.(我不想使用任何应用程序服务器或 cron 作业,我想保留我的网络应用程序中的上述内容.)

Until now I have used all of the above inside a ServletContextListener. (I don't want to use any application server or cron job and I want to keep the above things inside my web app.)

是否有一种聪明的 JSF 方法可以做到这一点,还是我应该坚持旧模式?

Is there a smart JSF way of doing this or should I stick with the old pattern?

推荐答案

简介

至于从 JSF 托管 bean 内部生成线程,如果您希望能够通过 #{managedBeanName}只有才有意义> 或通过 @ManagedProperty("#{managedBeanName}").你应该只确保你实现了 @PreDestroy 以确保在 web 应用程序即将关闭时关闭所有这些线程,就像您在 contextDestroyed() ServletContextListener(是的,你做了?).另请参阅 是否安全在 JSF 托管 bean 中启动一个新线程?

对于在 JSF 托管 bean 中使用 java.util.Timer,您应该绝对不要使用老式的Timer,但是现代 ScheduledExecutorService.Timer 存在以下主要问题,使其不适用于长时间运行的 Java EE Web 应用程序(引自 实践中的Java并发):

As to using java.util.Timer in a JSF managed bean, you should absolutely not use the old fashioned Timer, but the modern ScheduledExecutorService. The Timer has the following major problems which makes it unsuitable for use in a long running Java EE web application (quoted from Java Concurrency in Practice):

  • Timer 对系统时钟的变化很敏感,ScheduledExecutorService 不是.
  • Timer 只有一个执行线程,所以长时间运行的任务会延迟其他任务.ScheduledExecutorService 可以配置任意数量的线程.
  • TimerTask 中抛出的任何运行时异常都会杀死该线程,从而使 Timer 死亡,即计划任务将不再运行.ScheduledThreadExecutor 不仅可以捕获运行时异常,还可以让您根据需要处理它们.抛出异常的任务将被取消,但其他任务将继续运行.
  • Timer is sensitive to changes in the system clock, ScheduledExecutorService isn't.
  • Timer has only one execution thread, so long-running task can delay other tasks. ScheduledExecutorService can be configured with any number of threads.
  • Any runtime exceptions thrown in a TimerTask kill that one thread, thus making Timer dead, i.e. scheduled tasks will not run anymore. ScheduledThreadExecutor not only catches runtime exceptions, but it lets you handle them if you want. Task which threw exception will be canceled, but other tasks will continue to run.

除了书名之外,我还能想到更多的缺点:

Apart from the book quotes, I can think of more disadvantages:

  • 如果您忘记明确地cancel()Timer,那么它会在取消部署后继续运行.所以在重新部署一个新线程后,再次做同样的工作.等等.现在它已经变成了一个即发即忘",你不能再以编程方式取消它.您基本上需要关闭并重新启动整个服务器以清除以前的线程.

  • If you forget to explicitly cancel() the Timer, then it keeps running after undeployment. So after a redeploy a new thread is created, doing the same job again. Etcetera. It has become a "fire and forget" by now and you can't programmatically cancel it anymore. You'd basically need to shutdown and restart the whole server to clear out previous threads.

如果Timer 线程没有被标记为守护线程,那么它将阻止webapp 的卸载和服务器的关闭.你基本上需要硬杀死服务器.主要缺点是 webapp 将无法通过例如执行优雅的清理.contextDestroyed()@PreDestroy 方法.

If the Timer thread is not marked as daemon thread, then it will block the webapp's undeployment and server's shutdown. You'd basically need to hard kill the server. The major disadvantage is that the webapp won't be able to perform graceful cleanup via e.g. contextDestroyed() and @PreDestroy methods.

如果您面向 Java EE 6 或更新版本(例如 JBoss AS、GlassFish、TomEE 等,因此不是准系统 JSP/Servlet 容器,例如 Tomcat),则使用 @Singleton 带有 @Schedule 方法.这样,容器将担心自己通过 ScheduledExecutorService 来池化和销毁线程.您所需要的只是以下 EJB:

If you target Java EE 6 or newer (e.g. JBoss AS, GlassFish, TomEE, etc and thus not a barebones JSP/Servlet container such as Tomcat), then use a @Singleton EJB with a @Schedule method instead. This way the container will worry itself about pooling and destroying threads via ScheduledExecutorService. All you need is then the following EJB:

@Singleton
public class BackgroundJobManager {

    @Schedule(hour="0", minute="0", second="0", persistent=false)
    public void someDailyJob() {
        // Do your job here which should run every start of day.
    }

    @Schedule(hour="*/1", minute="0", second="0", persistent=false)
    public void someHourlyJob() {
        // Do your job here which should run every hour of day.
    }

    @Schedule(hour="*", minute="*/15", second="0", persistent=false)
    public void someQuarterlyJob() {
        // Do your job here which should run every 15 minute of hour.
    }

} 

如果需要,可以通过 在托管 bean 中使用@EJB:

This is if necessary available in managed beans by @EJB:

@EJB
private BackgroundJobManager backgroundJobManager;

EJB 不可用?使用 ScheduledExecutorService

如果没有 EJB,您需要手动使用 ScheduledExecutorService.应用程序范围的托管 bean 实现看起来像这样:

EJB unavailable? Use ScheduledExecutorService

Without EJB, you'd need to manually work with ScheduledExecutorService. The application scoped managed bean implementation would look something like this:

@ManagedBean(eager=true)
@ApplicationScoped
public class BackgroundJobManager {

    private ScheduledExecutorService scheduler; 

    @PostConstruct
    public void init() {
        scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(new SomeDailyJob(), 0, 1, TimeUnit.DAYS);
    }

    @PreDestroy
    public void destroy() {
        scheduler.shutdownNow();
    }

}

SomeDailyJob 看起来像这样:

public class SomeDailyJob implements Runnable {

    @Override
    public void run() {
        // Do your job here.
    }

}

如果您根本不需要在视图或其他托管 bean 中引用它,那么最好使用 ServletContextListener 以使其与 JSF 分离.

If you don't need to reference it in the view or other managed beans at all, then better just use ServletContextListener to keep it decoupled from JSF.

@WebListener
public class BackgroundJobManager implements ServletContextListener {

    private ScheduledExecutorService scheduler;

    @Override
    public void contextInitialized(ServletContextEvent event) {
        scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(new SomeDailyJob(), 0, 1, TimeUnit.DAYS);
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        scheduler.shutdownNow();
    }

}

这篇关于使用计时器在 JSF 托管 bean 中为计划任务生成线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆