Статьи

EJB 3.x: модели жизненного цикла и параллелизма (часть 2)

Это второй пост из двух частей серии. Первая часть посвящена жизненному циклу и параллельному поведению EJB с состоянием и без гражданства. Я расскажу о синглтоновских EJB в этом посте.

Паттерн Синглтон, пожалуй, наиболее часто используемый (иногда неправильно используемый) паттерн.

Одной тонны и люблю это!

Одной тонны и люблю это!

Java EE освобождает нас от написания явного кода (как на рисунке выше) для реализации шаблона Singleton. Синглтон EJB были введены в EJB 3.1, который сам был частью Java EE 6 . Все, что требуется, — это аннотация @ javax.ejb.Singleton (на уровне класса) (и еще несколько, если вы хотите уточнить другие аспекты — читайте дальше) в классе компонента, чтобы обозначить его как сессионный компонент Singleton.

В JVM есть один и только один экземпляр Singleton EJB — независимо от того, сколько клиентов к нему обращаются. Это не похоже на Stateful SB — один экземпляр компонента, подключенный к одному клиенту на протяжении всего его жизненного цикла, и не похоже на 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, например, синхронизированных, энергозависимых и т. Д.
  • Трудно получить право!

Пример кода

Давайте рассмотрим простой фрагмент кода, чтобы лучше понять изложенные выше факты:

Первый сценарий — управляемый контейнером параллелизм (по умолчанию тип блокировки не указан явно)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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());
 
    }
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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 (да, только два .. это скорее демонстрация, а не соревнование по нагрузочному тестированию!)

КМЦ-JMeter-1

КМЦ-2-JMeter

КМЦ-результат

наблюдения

Глядя на логи, можно легко разобрать следующее:

  • Сервлет, конечно, не является потокобезопасным, следовательно, два потока входят одновременно
  • Один из потоков входит в метод в классе компонента Singleton (отмечен красным), и дальнейший доступ запрещен из-за установленного контейнером типа блокировки WRITE по умолчанию.
  • Как только первый поток завершил выполнение, второй поток (отмеченный зеленым), который был первоначально заблокирован, получает возможность выполнить метод компонента Singleton
  • Довольно просто!

Сценарий второй — придерживаться управляемого контейнером параллелизма. Изменение явного типа блокировки с WRITE на READ

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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());
 
    }
}

Что происходит, когда приложение бомбардируется (каламбур!) С двумя одновременными потоками. , , ?

BMC-результат

  • Два потока входят в сервлет одновременно — как и ожидалось
  • Один из потоков входит в метод в классе компонента Singleton (отмечен красным)
  • Второй поток (отмеченный зеленым) также может одновременно ввести метод компонента Singleton (проверьте отметку времени).
  • Опять же — довольно просто!

Управляемый Bean параллелизм — это не то, что я сейчас изображаю. Как указывалось выше, использование BMC для Singleton передает ответственность разработчику, и он может свободно кодировать функции параллелизма в bean-компоненте — это можно сделать, просто используя синхронизацию для каждого метода или других механизмов, например, из java.util.concurrent API.

Предлагаемое чтение

Ура!