[英]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之间共享。
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运行时之外的边缘情况:
destroy
( fooBean
为null,现在已“缓存”在寄存器中) init
:在init之前销毁,这时我们处于CDI的第4维), destroy
( fooBean
已经缓存在寄存器中,因此value为null)。 要么
fooBean
的方法( fooBean
为null,现在已“缓存”在寄存器中) init
:T1已初始化,而fooBean
已被T2使用,这时我们处于CDI的第4维 destroy
( fooBean
已经缓存在寄存器中,因此value为null)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.