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

查看:141
本文介绍了使用计时器在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}的视图中引用它或在<托管的Bean中通过 @ManagedProperty("#{managedBeanName}") .您只应确保实现 @PreDestroy 以确保每当webapp即将关闭时,所有这些线程都被关闭,就像您在 contextDestroyed() 方法/ServletContextListener.html"rel =" noreferrer> ServletContextListener (是的,是吗?).另请参见在JSF托管bean中启动新线程?

Introduction

As to spawning a thread from inside a JSF managed bean, it would only make sense if you want to be able to reference it in your views by #{managedBeanName} or in other managed beans by @ManagedProperty("#{managedBeanName}"). You should only make sure that you implement @PreDestroy to ensure that all those threads are shut down whenever the webapp is about to shutdown, like as you would do in contextDestroyed() method of ServletContextListener (yes, you did?). See also Is it safe to start a new thread in a JSF managed bean?

关于在JSF托管bean中使用java.util.Timer,您应该绝对不使用老式的Timer,而应使用现代的实践中的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,则取消部署后它将继续运行.因此,在重新部署后,将创建一个新线程,再次执行相同的工作. Etcetera.到目前为止,它已成为一劳永逸"的事物,您无法通过编程方式将其取消.您基本上需要关闭并重新启动整个服务器,以清除以前的线程.

  • 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的取消部署和服务器的关闭.基本上,您需要强行终止服务器.主要缺点是该Web应用程序将无法通过以下方式执行正常的清除操作: 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),请使用

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.
    }

} 

如有必要, @EJB在托管bean中可用:

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中引用它,那么最好使用

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天全站免登陆