Контейнер 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");

Ну... решение Дэвида не сработало для меня по следующим причинам:

  1. Компилятор возился из-за того, что java.util.concurrent не разрешен... что имеет смысл в JBOSS.
  2. Также: общедоступный 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.

Другие вопросы по тегам