Контейнер Jboss Java EE и служба ExecutorService
У меня есть отдельное Java-приложение, которое использует ExecutorService для параллельной обработки нескольких заданий
ExecutorService es = Executors.newFixedThreadPool(10);
Теперь я хочу повторно использовать то же решение в EJB-компоненте, но не уверен, как правильно инициализировать ThreadPool, поскольку обычно я оставляю контейнер Java EE для управления всеми ресурсами потока. Могу ли я просто использовать тот же код или есть альтернативный правильный способ получения пула управляемых потоков Jboss?
5 ответов
Правильный способ сделать это в вашем EJB - использовать ManagedExecutorService, который является частью Concurrency Utils API (Java EE7). Вы не должны использовать какой-либо ExecutorService, который является частью java.util.concurrent в вашем корпоративном коде.
С помощью ManagedExecutorService ваш новый поток будет создан и управляться контейнером.
Следующий пример взят с моего сайта здесь.
Чтобы создать новый поток с помощью ManagedExecutorService, сначала создайте объект задачи, который реализует Callable. В методе call() мы определим работу, которую мы хотим выполнить в отдельном потоке.
public class ReportTask implements Callable<Report> {
Logger logger = Logger.getLogger(getClass().getSimpleName());
public Report call() {
try {
Thread.sleep(3000);
catch (InterruptedException e) {
logger.log(Level.SEVERE, "Thread interrupted", e);
}
return new Report();
}
}
Затем нам нужно вызвать задачу, передав ее в метод submit() ManagedExecutorService.
@Stateless
public class ReportBean {
@Resource
private ManagedExecutorService executorService;
public void runReports() {
ReportTask reportTask = new ReportTask();
Future<Report> future = executorService.submit(reportTask);
}
}
Обязательное предупреждение: создание собственных потоков на сервере приложений Java EE (даже Tomcat) не рекомендуется, поскольку это может привести к серьезным проблемам с производительностью и в большинстве случаев препятствует работе функциональных контейнеров, таких как JNDI. Новые потоки не будут знать, к какому приложению они принадлежат, загрузчик классов контекста Thread не будет установлен и многие другие скрытые проблемы.
К счастью, есть способ заставить сервер Java EE управлять пулом потоков через Java EE 6 @Asynchronous
и это умный дизайн шаблона. Переносим на любой сертифицированный сервер Java EE 6.
Создайте этот EJB в вашем приложении.
package org.superbiz;
import javax.ejb.Asynchronous;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
@Stateless(name="Executor")
public class ExecutorBean implements Executor {
@Asynchronous
@Override
public void execute(Runnable command) {
command.run();
}
}
Затем вы можете ссылаться на этот bean-компонент в другом месте приложения с помощью простого внедрения зависимостей (если ссылающийся компонент - Servlet, Listener, Filter, другой EJB, JSF-управляемый bean-компонент).
@EJB
private Executor executor;
Затем используйте Executor
как обычно.
Если компонент не является другим компонентом Java EE, вы можете искать компонент через:
InitialContext initialContext = new InitialContext();
Executor executor = (Executor) initialContext.lookup("java:module/Executor");
Ну... решение Дэвида не сработало для меня по следующим причинам:
- Компилятор возился из-за того, что java.util.concurrent не разрешен... что имеет смысл в JBOSS.
- Также: общедоступный STATIC класс...? Прочтите это: Почему вы не можете объявить класс статическим в Java?
Вот что я сделал:
Моя установка:
- JBOSS AS 7.1.1
- Java 1.6
- RHEL
- Выполнение примера с Gradle и Arquillian:
@Stateless
public class ExecutorBean {
@Asynchronous
public void execute(Runnable command) {
command.run();
}
}
Тогда ваш клиент выглядит так:
@EJB ExecutorBean eb;
@Test
public void testExecutorBean() {
eb.execute(new YourCustomizedRunnableWhichDoesALotOfUsefulStuff());
assertFalse(!true);
}
Остерегайтесь, однако: в моем standalone.xml (или, вообще говоря, моем конфигурационном файле для JBOSS, у меня есть раздел "пулы потоков". Посмотрите на него (если вы используете JBOSSAS) и повозитесь со значениями там. Узнайте как он себя ведет. Когда я использую потоки с тестами arquillian, я получаю потоки, которые убиты, хотя мое время активности активности очень велико. Я думаю, что это связано с тем, как arquillian microdeploys. Когда arquillian заканчивает работу, все незавершенные потоки уничтожаются, которые работали в то время как тесты выполняются... по крайней мере, это то, что я думаю, что я наблюдаю. С другой стороны, все законченные потоки фактически вели себя хорошо в том смысле, что они завершили свои задачи / операции.
Надеюсь, этот пост поможет!
До EE7 вы можете использовать WorkManager из JSR 237
http://docs.oracle.com/javaee/1.4/api/javax/resource/spi/work/WorkManager.html
Эта спецификация в настоящее время отменена, но некоторые серверы приложений реализуют ее. Я использую реализацию IBM в WebSphere 8.5 - IBM WorkManager. Это полностью управляемый ресурс, доступный в консоли администратора. Обратите внимание, что он не совместим по интерфейсу с Oracle.
Вот пример для версии IBM:
@Resource(lookup = "wm/default")
WorkManager workManager;
public void process() {
try {
ArrayList<WorkItem> workItems = new ArrayList<WorkItem>();
for (int i = 0; i < 100; i++) {
// submit 100 jobs
workItems.add(workManager.startWork(new Work() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " Running");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void release() {
System.out.println(Thread.currentThread().getName() + " Released");
}
}));
}
// wait for all jobs to be done.
workManager.join(workItems, WorkManager.JOIN_AND, 100000);
} catch (WorkException e) {
e.printStackTrace();
}
}
Также я знаю о Commonj Workmanager.
Если вы используете JBoss, вы можете использовать org.jboss.seam.async.ThreadPoolDispatcher.
ThreadPoolDispatcher полностью управляется.
Другие полезные управляемые классы см. В пакете: org.jboss.seam.async.