如何以固定的时间间隔运行后台作业方法?

时间:2013-04-03 12:39:18

标签: java java-ee tomcat servlets background-process

我在Apache Tomcat上使用JSP / Servlet。我必须每10分钟运行一次方法。我怎样才能做到这一点?

2 个答案:

答案 0 :(得分: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容器以及所有on em(如Glassfish,JBoss AS,TomEE等),那么您可以使用@Singleton EJB和@Schedule方法。这样容器就会担心汇集和销毁线程。您只需要以下EJB:

@Singleton
public class SomeTask {

    @Schedule(hour="*", minute="*/10", second="0", persistent=false)
    public void run() {
        // Do your job here.
    }

} 

请注意,通过这种方式,您可以通常的方式(@PersistenceContext等)继续透明地使用容器管理的事务,这对于ScheduledExecutorService是不可能的 - 您必须手动获取实体管理器和手动启动/提交/结束事务,但你默认情况下已经没有像Tomcat这样的准系统servlet容器上有另一个选项。

请注意,在所谓的“终身长期”运行Java EE Web应用程序中,绝不应使用Timer。它有以下主要问题,使其不适合在Java EE中使用(引自Java Concurrency in Practice):

  • Timer对系统时钟的变化很敏感,ScheduledExecutorService不是。
  • Timer只有一个执行线程,因此长时间运行的任务可以延迟其他任务。 ScheduledExecutorService可以配置任意数量的线程。
  • TimerTask中抛出的任何运行时异常都会导致一个线程死亡,从而导致Timer死亡,即计划任务将不再运行(直到重新启动服务器)。 ScheduledThreadExecutor不仅可以捕获运行时异常,还可以根据需要处理它们。抛出异常的任务将被取消,但其他任务将继续运行。

答案 1 :(得分:3)

继续阅读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();
    }
}

此外,您可以尝试使用Timer中的Java ServletContextListener,但不建议在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);

实现MyTask的{​​{1}}是一个实现TimerTask接口的类,因此您必须使用代码覆盖run方法:

Runnable