问题
Using a post construct approach when we want to conditionally initialise some of the bean's fields, do we need to care about volatility of the field, since it is a multithread environment?
Say, we have something like this:
@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();
}
}
}
So should the fooBean
be wrapped into, let's say, the AtomicReference
or be a volatile
or it would be a redundant extra protection?
P.S. In this particular case it can be reformulated as: are post construct and post destroy events performed by the same thread or not? However I would like to have an answer for a more general case.
回答1:
I would say it depends which thread is actually initiating and destroying the contexts.
If you use regular events, they are synchronous (asynchronous events have been added in CDI 2.0 with ObservesAsync
, see
Java EE 8: Sending asynchronous CDI 2.0 events with ManagedExecutorService ) so they are called in the same thread as the caller.
In general, I don't think the same thread is used (in application servers or standalone applications) so I would recommend using volatile
to ensure the right value is seen (basically the value constructed seen on destroy thread). However, it is not a use case happening so much to initiate and destroy your application in a concurrent way...
回答2:
FooService
is a singleton which is shared between all managed beans in the application.
Annotation Type ApplicationScoped
private FooBean fooBean
is a state of the singleton object.
By default, CDI does not manage concurrency so it is the responsibility of a developer.
In this particular case it can be reformulated as: are post construct and post destroy events performed by the same thread or not?
CDI specification does not restrict containers to use the same thread for initialization and destruction of the application context. This behavior is implementation specific. In general case those threads will be different because initialization happens on the thread handling the first request to the application but destruction happens on the thread handling request from management console.
回答3:
You may delegate concurrency management to EJB container - if your runtime environment includes one.
Neither volatile
nor AtomicReference
are needed in this case!
Following definition will do the job:
@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();
}
}
}
回答4:
According to the specification:
An event with qualifier @Initialized(ApplicationScoped.class) is synchronously fired when the application context is initialized.
An event with qualifier @BeforeDestroyed(ApplicationScoped.class) is synchronously fired when the application context is about to be destroyed, i.e. before the actual destruction.
An event with qualifier @Destroyed(ApplicationScoped.class) is synchronously fired when the application context is destroyed, i.e. after the actual destruction.
And according to this presentation Bean manager lifecycle: the lifecycle of the bean manager is synchronous between the different states of the process and the sequence is kept: "destroy not before init".
Jboss are the specification lead of CDI 2.0
I do not see any scenario that would require a volatile/protection. Even if T1 inits then T2 destroys, it will be T1 then T2, not T1 and T2 concurrently.
And even if it would be concurrently, to have an issue it would imply weird scenario, edge scenario outside the CDI runtime:
- T2 calls
destroy
(fooBean
is null and now 'cached' in a register) - Then T1 calls
init
: destroy before init, at this point we are in the 4th dimension of CDI), - Then T2 calls
destroy
(fooBean
is already cached in a register so is value is null)).
Or
- T2 calls a method that access
fooBean
(fooBean
is null and now 'cached' in a register) - Then T1 calls
init
: T1 is initialized whereasfooBean
has already been used by T2, at this point we are in the 4th dimension of CDI - Then T2 calls
destroy
(fooBean
is already cached in a register so is value is null)).
来源:https://stackoverflow.com/questions/53227585/cdi-postconstruct-and-volatile-fields