Введение
Создание потоков из управляемого bean-объекта области сеанса не обязательно является хаком, если оно выполняет ту работу, которую вы хотите. Но порождение нитей само по себе должно быть сделано с особой осторожностью. Код не должен быть написан таким образом, чтобы один пользователь мог, например, создавать неограниченное количество потоков в сеансе и / или чтобы потоки продолжали работать даже после того, как сеанс был разрушен. Рано или поздно оно взорвёт вашу заявку.
Код должен быть написан таким образом, чтобы вы могли гарантировать, что пользователь, например, никогда не сможет создавать более одного фонового потока за сеанс и что поток гарантированно будет прерван при каждом разрушении сеанса. Для выполнения нескольких задач в течение одного сеанса необходимо поставить их в очередь.
Кроме того, все эти потоки должны предпочтительно обслуживаться общим пулом потоков, чтобы можно было ограничить общее количество порождаемых потоков на уровне приложения. Средний сервер приложений Java EE предлагает управляемый контейнером пул потоков, который вы можете использовать, среди прочего, в EJB @Asynchronous
и @Schedule
. Чтобы быть независимым от контейнера, вы также можете использовать для этого Java 1.5 Util Concurrent ExecutorService
и ScheduledExecutorService
.
Ниже приведены примеры использования Java EE 6+ с EJB.
Запустить и забыть задачу при отправке формы
@Named
@RequestScoped // Or @ViewScoped
public class Bean {
@EJB
private SomeService someService;
public void submit() {
someService.asyncTask();
// ... (this code will immediately continue without waiting)
}
}
@Stateless
public class SomeService {
@Asynchronous
public void asyncTask() {
// ...
}
}
Асинхронное получение модели при загрузке страницы
@Named
@RequestScoped // Or @ViewScoped
public class Bean {
private Future<List<Entity>> asyncEntities;
@EJB
private EntityService entityService;
@PostConstruct
public void init() {
asyncEntities = entityService.asyncList();
// ... (this code will immediately continue without waiting)
}
public List<Entity> getEntities() {
try {
return asyncEntities.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new FacesException(e);
} catch (ExecutionException e) {
throw new FacesException(e);
}
}
}
@Stateless
public class EntityService {
@PersistenceContext
private EntityManager entityManager;
@Asynchronous
public Future<List<Entity>> asyncList() {
List<Entity> entities = entityManager
.createQuery("SELECT e FROM Entity e", Entity.class)
.getResultList();
return new AsyncResult<>(entities);
}
}
Если вы используете служебную библиотеку JSF OmniFaces , это можно сделать еще быстрее, если вы аннотируете управляемый компонент с помощью @Eager
.
Планирование фоновых заданий при запуске приложения
@Singleton
public class BackgroundJobManager {
@Schedule(hour="0", minute="0", second="0", persistent=false)
public void someDailyJob() {
// ... (runs every start of day)
}
@Schedule(hour="*/1", minute="0", second="0", persistent=false)
public void someHourlyJob() {
// ... (runs every hour of day)
}
@Schedule(hour="*", minute="*/15", second="0", persistent=false)
public void someQuarterlyJob() {
// ... (runs every 15th minute of hour)
}
@Schedule(hour="*", minute="*", second="*/30", persistent=false)
public void someHalfminutelyJob() {
// ... (runs every 30th second of minute)
}
}
Постоянно обновлять модель приложения в фоновом режиме
@Named
@RequestScoped // Or @ViewScoped
public class Bean {
@EJB
private SomeTop100Manager someTop100Manager;
public List<Some> getSomeTop100() {
return someTop100Manager.list();
}
}
@Singleton
@ConcurrencyManagement(BEAN)
public class SomeTop100Manager {
@PersistenceContext
private EntityManager entityManager;
private List<Some> top100;
@PostConstruct
@Schedule(hour="*", minute="*/1", second="0", persistent=false)
public void load() {
top100 = entityManager
.createNamedQuery("Some.top100", Some.class)
.getResultList();
}
public List<Some> list() {
return top100;
}
}
Смотри также: