[英]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.