繁体   English   中英

CDI PostConstruct和volatile字段

[英]CDI PostConstruct and volatile fields

当我们要有条件地初始化一些bean的字段时,使用后构造方法,因为这是一个多线程环境,我们是否需要关心字段的易变性?

说,我们有这样的事情:

@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还是多余的额外保护?

PS在这种特殊情况下,可以重新定义为:后构造和后破坏事件是否由同一线程执行? 但是,我想为一个更一般的情况提供一个答案。

我会说这取决于哪个线程实际上是在初始化和破坏上下文。 如果使用常规事件,则它们是同步的(异步事件已在CDI 2.0中通过ObservesAsync添加,请参见Java EE 8:使用ManagedExecutorService发送异步CDI 2.0事件 ),因此它们在与调用方相同的线程中被调用。

通常,我不认为使用同一线程(在应用程序服务器或独立应用程序中),因此我建议使用volatile以确保看到正确的值(基本上是在destroy线程上看到的构造值)。 但是,并不会以并发方式启动和销毁您的应用程序,这种情况并不多见。

FooService是一个单例,在应用程序中的所有托管Bean之间共享。

注释类型ApplicationScoped

private FooBean fooBean是单例对象的状态。

默认情况下,CDI不管理并发,因此这是开发人员的责任。

在这种特殊情况下,可以将其重新表示为:后构造和后破坏事件是否由同一线程执行?

CDI规范不限制容器使用同一线程来初始化和销毁​​应用程序上下文。 此行为是特定于实现的。 通常,这些线程会有所不同,因为初始化发生在处理对应用程序的第一个请求的线程上,但销毁发生在来自管理控制台的线程处理的请求上。

您可以将并发管理委托给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();
       }
    }
}

根据规格:

初始化应用程序上下文时,会同时触发带有限定符@Initialized(ApplicationScoped.class)的事件。

当应用程序上下文即将被销毁时,即在实际销毁之前,带有限定符@BeforeDestroyed(ApplicationScoped.class)的事件被同步激发。

当应用程序上下文被销毁时,即在实际销毁之后,带有限定符@Destroyed(ApplicationScoped.class)的事件被同步激发。

并且根据此演示, Bean管理器的生命周期Bean管理器的生命周期在流程的不同状态之间是同步的,并且保留了以下顺序:“销毁不是在初始化之前”。

Jboss是CDI 2.0的规范负责人

我看不到任何需要波动/保护的情况。 即使T1初始化然后T2销毁,也将是T1 然后 T2,而不是同时T1和T2。

而且即使是同时发生的问题,也暗示着奇怪的情况,即CDI运行时之外的边缘情况:

  • T2调用destroyfooBean为null,现在已“缓存”在寄存器中)
  • 然后T1调用init :在init之前销毁,这时我们处于CDI的第4维),
  • 然后T2调用destroyfooBean已经缓存在寄存器中,因此value为null)。

要么

  • T2调用访问fooBean的方法( fooBean为null,现在已“缓存”在寄存器中)
  • 然后T1调用init :T1已初始化,而fooBean已被T2使用,这时我们处于CDI的第4维
  • 然后T2调用destroyfooBean已经缓存在寄存器中,因此value为null)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM