Введение
Что касается порождения потока внутри управляемого JSF-компонента, то будет только иметь смысл, если вы захотите ссылаться на него в своих представлениях с помощью #{managedBeanName}
илив других управляемых бобах на @ManagedProperty("#{managedBeanName}")
.Вам следует только убедиться, что вы внедрили @PreDestroy
, чтобы все эти потоки закрывались всякий раз, когда веб-приложение должно было завершиться, как вы это делали в методе contextDestroyed()
из ServletContextListener
(да, вы сделали?).См. Также Безопасно ли начинать новый поток в управляемом компоненте JSF?
Никогда не использовать java.util.Timer
в Java EE
Что касается использования java.util.Timer
вУправляемый компонент JSF, вы должны абсолютно не использовать старомодный Timer
, а современный ScheduledExecutorService
.У Timer
есть следующие основные проблемы, которые делают его непригодным для использования в долго работающем веб-приложении Java EE (цитата из Java Concurrency на практике ):
Timer
чувствителен к изменениям системных часов, ScheduledExecutorService
нет. Timer
имеет только один поток выполнения, поэтому длительное выполнение задачи может задержать выполнение других задач.ScheduledExecutorService
может быть настроен с любым количеством потоков. - Любые исключения времени выполнения, сгенерированные в
TimerTask
, уничтожают этот один поток, таким образом делая Timer
мертвым, то есть запланированные задачи больше не будут выполняться.ScheduledThreadExecutor
не только перехватывает исключения во время выполнения, но и позволяет вам обрабатывать их, если хотите.Задача, вызвавшая исключение, будет отменена, но другие задачи продолжат выполняться.
Помимо цитат из книг, я могу подумать и о других недостатках:
Если вы забыли явно cancel()
Timer
, то он продолжает работать после отмены развертывания.Поэтому после повторного развертывания создается новый поток, снова выполняющий ту же работу.Etcetera.К настоящему моменту он стал «огнем и забыл», и вы больше не можете программно отменить его.В основном вам нужно было бы закрыть и перезапустить весь сервер, чтобы очистить предыдущие потоки.
Если поток Timer
не помечен как поток демона, он заблокирует отключение веб-приложения и остановку сервера.Вам в основном нужно жестко убить сервер.Основным недостатком является то, что веб-приложение не сможет выполнить изящную очистку, например, методами contextDestroyed()
и @PreDestroy
.
EJB доступен?Используйте @Schedule
Если вы нацелены на Java EE 6 или новее (например, JBoss AS, GlassFish, TomEE и т. Д. И, следовательно, не пустой контейнер JSP / Servlet, такой как Tomcat), тогда используйте@Singleton
EJB вместо метода @Schedule
.Таким образом, контейнер будет беспокоиться о пуле и уничтожении потоков через 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
:
@EJB
private BackgroundJobManager backgroundJobManager;
EJB недоступен?Используйте ScheduledExecutorService
Без EJB вам придется вручную работать с ScheduledExecutorService
.Реализация управляемого компонента в области приложения будет выглядеть примерно так:
@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.
@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();
}
}