如何以固定的时间间隔运行后台作业方法?
我在Apache Tomcat上使用JSP / Servlet。 我必须每10分钟运行一次方法。 我怎样才能做到这一点?
正如您在Tomcat上,它只是一个准系统servlet容器,您不能使用EJB的@Schedule
,这是Java EE规范推荐的。 您最好的选择是Java 1.5的java.util.concurrent
包中的ScheduledExecutorService
。 您可以在ServletContextListener
帮助下触发此操作,如下所示:
@WebListener public class BackgroundJobManager implements ServletContextListener { private ScheduledExecutorService scheduler; @Override public void contextInitialized(ServletContextEvent event) { scheduler = Executors.newSingleThreadScheduledExecutor(); scheduler.scheduleAtFixedRate(new SomeTask(), 0, 10, TimeUnit.MINUTES); } @Override public void contextDestroyed(ServletContextEvent event) { scheduler.shutdownNow(); } }
SomeTask
类看起来像这样:
public class SomeTask implements Runnable { @Override public void run() { // Do your job here. } }
如果您实际上正在使用具有EJB支持的真实Java EE容器以及所有的em(如Glassfish,JBoss AS,TomEE等),那么您可以使用带有@Schedule
方法的@Singleton
EJB。 这样容器就会担心汇集和销毁线程。 您只需要以下EJB:
@Singleton public class SomeTask { @Schedule(hour="*", minute="*/10", second="0", persistent=false) public void run() { // Do your job here. } }
请注意,通过这种方式,您可以通过常规方式( @PersistenceContext
等)透明地继续使用容器管理的事务,这对于ScheduledExecutorService
是不可能的 – 您必须手动获取实体管理器并手动启动/提交/结束事务,但是你默认情况下已经没有像Tomcat这样的准系统servletcontainer上有另一个选项了。
请注意,在所谓的“终身长”运行的Java EE Web应用程序中,绝不应使用Timer
。 它有以下主要问题,使其不适合在Java EE中使用(引自Java Concurrency in Practice ):
-
Timer
对系统时钟的变化很敏感,而ScheduledExecutorService
则不然。 -
Timer
只有一个执行线程,因此长时间运行的任务可以延迟其他任务。ScheduledExecutorService
可以配置任意数量的线程。 -
TimerTask
抛出的任何运行时exception会杀死一个线程,从而导致Timer
死机,即计划任务将不再运行(直到重新启动服务器)。ScheduledThreadExecutor
不仅捕获运行时exception,还允许您根据需要处理它们。 抛出exception的任务将被取消,但其他任务将继续运行。
读取ScheduledExecutorService,它必须由ServletContextListener启动
public class MyContext implements ServletContextListener { private ScheduledExecutorService sched; @Override public void contextInitialized(ServletContextEvent event) { sched = Executors.newSingleThreadScheduledExecutor(); sched.scheduleAtFixedRate(new MyTask(), 0, 10, TimeUnit.MINUTES); } @Override public void contextDestroyed(ServletContextEvent event) { sched.shutdownNow(); } }
此外,您可以尝试从ServletContextListener使用Java Timer ,但不建议在Java EE容器中使用它,因为它会从容器中删除对Thread资源的控制。 (ScheduledExecutorService的第一个选项是要走的路)。
Timer timer = new Timer("MyTimer"); MyTask t = new MyTask(); //Second Parameter is the specified the Starting Time for your timer in //MilliSeconds or Date //Third Parameter is the specified the Period between consecutive //calling for the method. timer.schedule(t, 0, 1000*60*10);
实现TimerTask
MyTask
是一个实现Runnable
接口的类,因此您必须使用您的代码覆盖run方法:
class MyTask extends TimerTask { public void run() { // your code here } }