Это второй пост из двух частей серии. Первая часть покрыла жизненный цикл и поведение параллелизма Stateful и лица без EJBs. Я расскажу о синглтоновских EJB в этом посте
Паттерн Синглтон, пожалуй, наиболее часто используемый (иногда неправильно используемый ;-)).
Java EE освобождает нас от написания явного кода (как на рисунке выше) для реализации шаблона Singleton. Синглтон EJB были введены в EJB 3.1, который сам был частью Java EE 6 . Все, что требуется, — это аннотация @ javax.ejb.Singleton (на уровне класса) (и еще несколько, если вы хотите уточнить другие аспекты — читайте дальше) в классе компонента, чтобы обозначить его как сессионный компонент Singleton.
В JVM есть один и только один экземпляр Singleton EJB — независимо от того, сколько клиентов к нему обращаются. Это не похоже на Stateful SB — один экземпляр компонента, подключенный к одному клиенту на протяжении всего его жизненного цикла, и не похоже на Stateless SB — новый экземпляр для каждого запроса клиента
Каковы различные состояния в жизненном цикле одноэлементного сессионного компонента?
Жизненный цикл для компонентов Singleton такой же, как и для сеансовых компонентов без сохранения состояния — фактически это один из более простых аспектов этого типа компонентов.
- Не существует
- готовы
Как меняются штаты? Что их вызывает?
Вот быстрый снимок в виде таблицы и диаграмма высокого уровня. , ,
Государственный переход | Триггеры | Callbacks |
---|---|---|
DNE к R | Когда к экземпляру впервые обращаются через JNDI / DI или автоматически создают его контейнером, используя @Startup или @DependsOn | @PostConstruct |
R в DNE | Контейнер завершает работу — уничтожает экземпляр компонента или в случае возникновения исключения в аннотированном методе @PostConstruct | @PreDestroy |
Примечание : DNE — не существует, R — готово
Как указывалось ранее, жизненный цикл является одной из более простых функций компонентов Singleton. Важно понимать их аспекты параллелизма.
Singleton Session Beans: управление параллелизмом
Как уже говорилось, у Синглтона есть только один экземпляр в JVM. В среде Java EE одновременный доступ неизбежен — поэтому мы в первую очередь используем такую технологию, как Java EE! ? Нужно убедиться, что стратегии параллелизма ( блокировки ) по сравнению с компонентами Singleton хорошо продуманы, в зависимости от варианта использования и требований
Параллельный бин Singleton можно разделить на 2 основные категории
- Управляемый контейнер (по умолчанию)
- Bean Managed
Управляемый контейнером параллелизм
- Как следует из названия, контейнер применяет разумные конфигурации по умолчанию для bean-компонента
- Может управляться с помощью аннотаций, а также XML (дескрипторы развертывания)
- Явно объявлено с использованием аннотации @ javax.ejb.ConcurrencyManagement для самого класса бина
- Значением по умолчанию является javax.ejb.ConcurrencyManagementType.CONTAINER
- Контейнер предоставляет две возможные стратегии блокировки — применимые как к классу бина, так и к его отдельным методам
- @ javax.ejb.Lock со значением javax.ejb.LockType.READ — разрешает параллельный доступ без блокировок записи
- @ javax.ejb.Lock со значением javax.ejb.LockType.WRITE (по умолчанию ) — гарантирует эксклюзивный доступ — только один поток может выполнить метод бина в данной точке
- @ javax.ejb.AccessTimeout может быть указан в классе или методе bean-компонента, чтобы гарантировать, что поток не блокирует и не удерживает блокировку в течение неопределенного промежутка времени.
Бин Управляемый Параллелизм
- Название ясно указывает — аспекты параллелизма bean-компонента оставлены на усмотрение разработчика. Имеет смысл, когда требуется более точное управление параллелизмом по сравнению с тем, что было предложено контейнером через вышеупомянутые конструкции
- Требуется использование соответствующих конструкций параллелизма Java, например, синхронизированных, энергозависимых и т. Д.
- Трудно получить право!
Пример кода
Давайте рассмотрим простой фрагмент кода, чтобы лучше понять изложенные выше факты
Первый сценарий — управляемый контейнером параллелизм (по умолчанию тип блокировки не указан явно)
package com.abhirockzz.wordpress.ejb.lifecycle.singleton; import com.abhirockzz.wordpress.ejb.lifecycle.stateful.MyStatefulBean; import java.util.Date; import java.util.logging.Level; import java.util.logging.Logger; import javax.ejb.Singleton; import javax.ejb.Startup; @Singleton @Startup public class MySingletonBean { public void act() { System.out.println("Entered MySingletonBean/act() on " + new Date().toString() + " . Singleton instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException ex) { Logger.getLogger(MyStatefulBean.class.getName()).log(Level.SEVERE, null, ex); } System.out.println("Exit MySingletonBean/act() on " + new Date().toString() + " . Singleton instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName()); } }
package com.abhirockzz.wordpress.ejb.lifecycle.singleton; import java.io.IOException; import java.util.Date; import javax.inject.Inject; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(name = "SingletonTestServlet", urlPatterns = {"/SingletonTestServlet"}) public class SingletonTestServlet extends HttpServlet { public SingletonTestServlet() { } @Inject MySingletonBean mySingleton; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("Entered SingletonTestServlet/doGet() on " + new Date().toString() + " . Servlet instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName()); mySingleton.act(); } }
Используя Apache JMeter — я запустил 2 параллельных потока на SingletonTestServlet (да, только два .. это скорее демонстрация, а не соревнование по нагрузочному тестированию ;-))
наблюдения
Глядя на логи, можно легко разобрать следующее
- Сервлет, конечно, не является потокобезопасным, следовательно, два потока входят одновременно
- Один из потоков входит в метод в классе компонента Singleton (отмечен красным), и дальнейший доступ запрещен из-за установленного контейнером типа блокировки WRITE по умолчанию.
- Как только первый поток завершил выполнение, второй поток (отмеченный зеленым), который был первоначально заблокирован, получает возможность выполнить метод компонента Singleton
- Довольно просто!
Сценарий второй — придерживаться управляемого контейнером параллелизма. Изменение явного типа блокировки с WRITE на READ
import com.abhirockzz.wordpress.ejb.lifecycle.stateful.MyStatefulBean; import java.util.Date; import java.util.logging.Level; import java.util.logging.Logger; import javax.ejb.ConcurrencyManagement; import javax.ejb.ConcurrencyManagementType; import javax.ejb.Lock; import javax.ejb.LockType; import javax.ejb.Singleton; import javax.ejb.Startup; @Singleton @Startup @ConcurrencyManagement(ConcurrencyManagementType.CONTAINER) public class MySingletonBean { @Lock(LockType.READ) public void act() { System.out.println("Entered MySingletonBean/act() on " + new Date().toString() + " . Singleton instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException ex) { Logger.getLogger(MyStatefulBean.class.getName()).log(Level.SEVERE, null, ex); } System.out.println("Exit MySingletonBean/act() on " + new Date().toString() + " . Singleton instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName()); } }
Что происходит, когда приложение бомбардируется (каламбур!) С двумя одновременными потоками. , , ?
- Два потока входят в сервлет одновременно — как и ожидалось
- Один из потоков входит в метод в классе компонента Singleton (отмечен красным)
- Второму потоку (отмеченному зеленым) также удается одновременно ввести метод компонента Singleton (проверьте отметку времени)
- Опять же — довольно просто! ?
Управляемый Bean параллелизм — это не то, что я сейчас изображаю. Как указывалось выше, использование BMC для Singleton передает ответственность разработчику, и он может свободно кодировать функции параллелизма в bean-компоненте — это можно сделать, просто используя синхронизацию для каждого метода или других механизмов, например, из java.util.concurrent API.