簡體   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