产生JSF托pipebean中的线程以使用计时器进行计划任务
我想知道是否可以在应用程序范围的bean中使用Timer
。
例如,可以说我想创build一个计时器任务,每天向每个注册成员发送一堆电子邮件。 我试图尽可能多地使用JSF,我想知道这是否可以接受(我知道,这听起来有点奇怪)。
到目前为止,我已经在ServletContextListener
使用了上述ServletContextListener
。 (我不想使用任何应用程序服务器或cron作业,我想保留上面的东西在我的web应用程序。)
有没有一个聪明的JSF这样做的方式还是应该坚持旧的模式?
介绍
至于从JSF托pipebean中产生一个线程, 只有当你想在你的视图中通过#{managedBeanName}
或在@ManagedProperty("#{managedBeanName}")
其他托pipebean中引用它@ManagedProperty("#{managedBeanName}")
。 您应该确保实现@PreDestroy
以确保在webapp将要closures时closures所有这些线程,就像在contextDestroyed()
方法中一样(是的,您做了?)。 另请参见在JSF托pipebean中启动新线程是否安全?
切勿在Java EE中使用java.util.Timer
至于在JSF托pipebean中使用java.util.Timer
, 绝对不应该使用旧式的Timer
,而应该使用现代的ScheduledExecutorService
。 Timer
具有以下主要问题,使其不适用于长时间运行的Java EE Web应用程序(引自“ Java并发实践” ):
-
Timer
对系统时钟的变化很敏感,ScheduledExecutorService
不是。 -
Timer
只有一个执行线程,所以长时间运行的任务可以延迟其他任务。ScheduledExecutorService
可以configuration任意数量的线程。 - 在
TimerTask
引发的任何运行时exception都会杀死一个线程,从而导致Timer
死机,即计划任务将不再运行。ScheduledThreadExecutor
不仅捕获运行时exception,但它可以让你处理它们,如果你想。 抛出exception的任务将被取消,但其他任务将继续运行。
除了书中的引言之外,我可以想到更多的缺点:
-
如果您忘记显式
cancel()
Timer
,那么在取消部署之后它会继续运行。 所以在重新部署一个新的线程后,再次做同样的工作。 等等。 它现在已经成为一个“火灾和遗忘”,你不能以编程方式取消它。 你基本上需要closures并重新启动整个服务器清除以前的线程。 -
如果
Timer
线程没有标记为守护进程线程,那么它将阻止Web应用程序的取消部署和服务器closures。 你基本上需要努力杀死服务器。 主要的缺点是web应用程序将无法通过例如contextDestroyed()
和@PreDestroy
方法执行优雅的清理。
EJB可用? 使用@Schedule
如果你的目标是Java EE 6或更新版本(例如JBoss AS,GlassFish,TomEE等,因此不是像Tomcat这样的准系统JSP / Servlet容器),那么可以使用带有@Schedule
方法的@Singleton
EJB。 这样容器就会担心通过ScheduledExecutorService
集中和销毁线程。 所有你需要的是以下的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
托pipe的豆:
@EJB private BackgroundJobManager backgroundJobManager;
EJB不可用? 使用ScheduledExecutorService
没有EJB,你需要手动使用ScheduledExecutorService
。 应用程序作用域的托pipebean实现看起来像这样:
@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. } }
如果您不需要在视图或其他托pipe的bean中引用它,那么最好使用ServletContextListener
使其与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(); } }