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