Порождение потоков в управляемом компоненте JSF для запланированных задач с использованием таймера
Я хотел бы знать, нормально ли это использовать Timer
внутри приложения определены области фасоли.
Например, допустим, я хочу создать задачу таймера, которая отправляет кучу писем каждому зарегистрированному участнику один раз в день. Я пытаюсь использовать как можно больше JSF, и я хотел бы знать, приемлемо ли это (это звучит немного странно, я знаю).
До сих пор я использовал все вышеперечисленное внутри ServletContextListener
, (Я не хочу использовать какой-либо сервер приложений или работу cron, и я хочу оставить вышеупомянутые вещи в своем веб-приложении.)
Есть ли умный способ JSF сделать это или я должен придерживаться старого шаблона?
1 ответ
Вступление
Что касается порождения потока внутри управляемого JSF-компонента, это будет иметь смысл, только если вы захотите ссылаться на него в своих представлениях #{managedBeanName}
или в других управляемых бобах @ManagedProperty("#{managedBeanName}")
, Вы должны только убедиться, что вы реализуете @PreDestroy
чтобы убедиться, что все эти потоки закрыты всякий раз, когда веб-приложение собирается завершить работу, как вы это делаете в contextDestroyed()
метод ServletContextListener
(да вы сделали?). См. Также Безопасно ли начинать новый поток в управляемом компоненте JSF?
Никогда не используйте java.util.Timer
в Java EE
Что касается использования java.util.Timer
в управляемом компоненте JSF абсолютно не следует использовать старомодный Timer
, но современный ScheduledExecutorService
, Timer
имеет следующие основные проблемы, которые делают его непригодным для использования в долго работающем веб-приложении Java EE (цитируется на Java Concurrency in Practice):
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();
}
}