Статьи

Современный параллелизм и Java EE

Управление собственными потоками в контейнере Java EE не рекомендуется и даже незаконно в некоторых обстоятельствах. Но что же нас оставляет, когда мы хотим использовать новые блестящие среды параллелизма Java, такие как Fork / Join и Akka, в приложениях Java EE? Добавление новых средств параллелизма в спецификацию Java EE 7 может открыть некоторые двери.

В прошлом году я изучал новые возможности параллелизма в JVM. Java SE 7 принесла нам параллелизм Fork / Join , и другие подходы, такие как параллелизм на основе акторов, например, с Akka, также стали популярными. Я немного поговорил о Fork / Join (разговор о Parleys , слайдах ) и Akka (разговор о Parleys , слайдах ), и один вопрос, который я всегда получаю: «Может ли это быть использовано в приложениях Java EE?»

Поскольку Fork / Join и Akka запускают и останавливают свои собственные потоки (пулы), ответ таков: «Обычно это работает, но не рекомендуется в контейнерах Java EE». Неудовлетворительный ответ? Вы ставите. Но, к счастью, все может измениться со следующим выпуском Java EE.

JSR-236: утилиты параллелизма для платформы Java EE

Большинство серверов приложений уже предлагают возможность получать управляемый (своего рода, своего рода) пул потоков через API WorkManager . Он никогда не был стандартизирован как часть Java EE. И даже если бы это было так, этого было бы недостаточно для платформы Fork / Join. Единственная возможность подключения, которую вы получаете при создании экземпляра ForkJoinPool, — это фабрика пользовательских потоков. Для записи, кажется, что Akka можно настроить с помощью WorkManager.

Однако стандартизация потоков, управляемых контейнером, может быть нашей задачей. Работа над JSR-236 началась в 2003 году. Да, это почти десять лет назад. Первый проект спецификации Concurrency EE появился в 2006 году, но, к сожалению, с тех пор он томился. Однако в апреле этого года JSR был реанимирован, а в прошлом месяце была опубликована обновленная версия . Несмотря на то, что разница с черновиком 2006 года минимальна (например, «обновить имя пакета с javax.util.concurrent до javax.enterprise.concurrent»), приятно видеть, что все снова в повестке дня.

javax.enterprise.concurrent

Так что же в этом неуловимом новом пакете javax.enterprise.concurrent? Я спас следующую диаграмму из презентации JavaOne 2006 года с новыми средствами параллелизма, помеченными желтым:

Java EE параллелизм

Двумя наиболее важными интерфейсами для включения альтернативных сред параллелизма являются ManagedExecutorService и ManagedThreadFactory , которые расширяют свои неуправляемые аналоги java.util.concurrent из Java SE. Что нам дает этот управляемый префикс? Это означает, что переданные задачи и потоки, созданные через эти интерфейсы, будут полностью осведомлены о контекстных сервисах Java EE. Такие вещи, как управление транзакциями, контекст безопасности и т. Д., Становятся доступными при выполнении задачи или использовании потока. Контейнер полностью осведомлен о жизненном цикле этих управляемых потоков и может предлагать общие параметры мониторинга и настройки.

Еще один интересный аспект предполагаемого ManagedExecutorService заключается в том, что он должен поддерживать распространение. Можно настроить одно из следующих свойств:

  • Локальный: задача запускается в том же процессе / сервере, который отправил задачу.
  • Распространяемый: задача может быть запущена на любом процессе / сервере, включая тот, который отправил задачу.
  • Распространяется с помощью Affinity: задача может быть запущена на любом процессе / сервере, включая тот, который отправил задачу. Все задачи будут выполняться на выбранном процессе / сервере.

Внезапно у нас есть строительные блоки для асинхронного распределенного вычислительного механизма! Очевидно, что задачи должны быть сериализуемыми, чтобы это работало.

Соединение точек

Вы можете задаться вопросом, почему важно иметь эти утилиты для параллелизма, поскольку Java EE 6 уже предоставляет аннотацию @Asynchronous для EJB, а также доступна поддержка асинхронных сервлетов . Какими бы полезными они ни были для разработки приложений, конструкции не предназначены для начальной загрузки других библиотек параллелизма и сред. Однако с помощью предложенных утилит вы можете создать свой собственный java.util.concurrent.ThreadPoolExecutor, поддерживаемый управляемыми потоками:

 // Can also use JNDI lookup in method instead of injection
 @Resource
 ManagedThreadFactory mtf;

 public ExecutorService getManagedThreadPool() {
   // All threads created will be managed
   return new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS,
       new ArrayBlockingQueue<Runnable>(10), tf);
 }

Точно так же ForkJoinPool может быть параметризован с помощью пользовательской реализации ForkJoinPool.ForkJoinWorkerThreadFactory, которая делегирует создание потока в ManagedThreadFactory. При попытке реализовать такой адаптер я, к сожалению, столкнулся с тем, что Fork / Join использует собственный подкласс Thread ( ForkJoinWorkerThread ). Это усложняет задачу, так как конструктор защищен пакетами. И, очевидно, я не могу протестировать код, так как я не знаю ни одной (публичной) реализации JSR-236. Кроме того, я считаю, что такие адаптеры для java.util.concurrent должны быть частью спецификации. Не нужно заставлять разработчиков приложений изобретать велосипед снова и снова!

Что теперь?

При чтении спецификации JSR-236 становится ясно, что она была создана в древние времена J2EE. Просто посмотрите на примеры кода в черновой спецификации (правда, интерфейсы EJBHome?). CDI никогда не упоминается, равно как и Fork / Join или другие соответствующие новые технологии. Тем не менее, ясно, что эта спецификация заложит основу для интеграции современных сред параллелизма Java с Java EE. Но как именно это получится, пока рано говорить.

Другим очевидным кандидатом на использование функциональности javax.enterprise.concurrent является пакетная обработка JSR. Кажется, есть много работы, чтобы сделать. Будем надеяться, что группы экспертов найдут достаточно времени для объединения усилий и продвижения платформы Java EE.