Статьи

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

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

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

singleton1

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

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 хорошо продуманы, в зависимости от варианта использования и требований

синглтон-12yr

Синглтон — потреблять с осторожностью!

Параллельный бин 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 (да, только два .. это скорее демонстрация, а не соревнование по нагрузочному тестированию ;-))

КМЦ-JMeter-1

КМЦ-2-JMeter

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

наблюдения

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

  • Сервлет, конечно, не является потокобезопасным, следовательно, два потока входят одновременно
  • Один из потоков входит в метод в классе компонента 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());
 
    }
}

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

BMC-результат

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

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