CDI PostConstruct и волатильные поля - PullRequest
0 голосов
/ 09 ноября 2018

Используя подход после конструирования, когда мы хотим условно инициализировать некоторые поля бина, нужно ли нам заботиться о волатильности поля, поскольку это многопоточная среда?

Скажем, у нас есть что-то вроде этого:

@ApplicationScoped
public class FooService {

    private final ConfigurationService configurationService;

    private FooBean fooBean;

    @Inject
    FooService(ConfigurationService configurationService) {
         this.configurationService = configurationService;
    }

    void init(@Observes @Initialized(ApplicationScoped.class) Object ignored) {
        if (configurationService.isFooBeanInitialisationEnabled()) {
             fooBean = initialiseFooBean(configurationService); // some initialisation
        }
    }

    void cleanup(@Observes @Destroyed(ApplicationScoped.class) Object ignored) {
       if (fooBean != null) {
           fooBean.cleanup();
       }
    }
}

Значит, fooBean должен быть заключен, скажем, в AtomicReference или быть volatile, или это будет избыточная дополнительная защита?

P.S. В данном конкретном случае его можно переформулировать следующим образом: выполняются ли события после пост-конструкции и после уничтожения одним и тем же потоком или нет? Однако я хотел бы получить ответ для более общего случая.

Ответы [ 4 ]

0 голосов
/ 12 ноября 2018

Согласно спецификации:

Событие с квалификатором @Initialized (ApplicationScoped.class) запускается синхронно при инициализации контекста приложения.

Событие с квалификатором @BeforeDestroyed (ApplicationScoped.class) запускается синхронно, когда контекст приложения собирается быть уничтоженным, то есть до фактического уничтожения.

Событие с квалификатором @Destroyed (ApplicationScoped.class) запускается синхронно при разрушении контекста приложения, т.е. после фактического уничтожения.

И в соответствии с этой презентацией Жизненный цикл менеджера бинов : жизненный цикл менеджера бинов является синхронным между различными состояниями процесса, и последовательность сохраняется: "уничтожить не перед инициализацией".

Jboss - это спецификация CDI 2.0

Я не вижу ни одного сценария, который потребовал бы энергозависимости / защиты. Даже если T1 inits, тогда T2 разрушает, это будет T1 тогда T2, а не T1 и T2 одновременно.

И даже если бы это было одновременно, возникновение проблемы означало бы странный сценарий, пограничный сценарий вне среды выполнения CDI:

  • T2 звонки destroy (fooBean имеет значение null и теперь «кэшируется» в регистре)
  • Затем T1 вызывает init: уничтожить перед init, в этот момент мы находимся в 4-м измерении CDI),
  • Затем T2 вызывает destroy (fooBean уже кэшируется в регистре, поэтому значение равно нулю)).

Или

  • T2 вызывает метод, который обращается к fooBean (fooBean имеет значение null и теперь «кэшируется» в регистре)
  • Затем T1 вызывает init: T1 инициализируется, тогда как fooBean уже используется T2, на данный момент мы находимся в 4-м измерении CDI
  • Затем T2 вызывает destroy (fooBean уже кэшируется в регистре, поэтому значение равно нулю)).
0 голосов
/ 09 ноября 2018

FooService - это синглтон, который используется всеми управляемыми компонентами в приложении.

Тип аннотации ApplicationScoped

private FooBean fooBean - это состояние одноэлементного объекта.

По умолчанию CDI не управляет параллелизмом, поэтому ответственность за него несет разработчик.

В данном конкретном случае его можно переформулировать следующим образом: выполняются ли события post construct и post destroy одной и той же цепочкой или нет?

Спецификация CDI не ограничивает контейнеры для использования одного и того же потока для инициализации и уничтожения контекста приложения. Это поведение зависит от реализации. В общем случае эти потоки будут другими, поскольку инициализация происходит в потоке, обрабатывающем первый запрос к приложению, но уничтожение происходит в запросе обработки потока из консоли управления.

0 голосов
/ 10 ноября 2018

Вы можете делегировать управление параллелизмом в контейнер EJB - если ваша среда выполнения включает его.

В этом случае volatile и AtomicReference не нужны!

Следующее определение сделает работу:

@javax.ejb.Startup   // initialize on application start
@javax.ejb.Singleton // EJB Singleton
public class FooService {

    private final ConfigurationService configurationService;

    private FooBean fooBean;

    @javax.inject.Inject
    FooService(ConfigurationService configurationService) {
         this.configurationService = configurationService;
    }


    @javax.annotation.PostConstruct
    void init() {
        if (configurationService.isFooBeanInitialisationEnabled()) {
             fooBean = initialiseFooBean(configurationService); // some initialisation
        }
    }

    @javax.annotation.PreDestroy
    void cleanup() {
       if (fooBean != null) {
           fooBean.cleanup();
       }
    }
}
0 голосов
/ 09 ноября 2018

Я бы сказал, что это зависит от того, какой поток фактически инициирует и разрушает контексты. Если вы используете обычные события, они являются синхронными (асинхронные события были добавлены в CDI 2.0 с ObservesAsync, см. Java EE 8: отправка асинхронных событий CDI 2.0 с ManagedExecutorService ), поэтому они вызываются в том же потоке, что и вызывающая сторона.

В общем, я не думаю, что используется один и тот же поток (на серверах приложений или в автономных приложениях), поэтому я бы рекомендовал использовать volatile, чтобы убедиться, что правильное значение видно (в основном значение, созданное в потоке уничтожения). Тем не менее, это не тот случай использования, который бы вызывал и одновременно уничтожал ваше приложение ...

...