Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I mark object attributes as volatile if I init them in @PostConstruct in Spring Framework?

Tags:

Suppose, that I do some initialization in Spring singleton bean @PostConstruct (simplified code):

@Service class SomeService {   public Data someData; // not final, not volatile    public SomeService() { }    @PostConstruct   public void init() {      someData = new Data(....);   } } 

Should I worry about someData visibility to other beans and mark it volatile?

(suppose that I cannot initialize it in constructor)

And second scenario: what if I overwrite value in @PostConstruct (after for example explicit initialization or initialization in constructor), so write in @PostConstruct will not be first write to this attribute?

like image 339
Piotr Müller Avatar asked May 28 '14 08:05

Piotr Müller


People also ask

What is the use of @PostConstruct in spring boot?

When we annotate a method in Spring Bean with @PostConstruct annotation, it gets executed after the spring bean is initialized. We can have only one method annotated with @PostConstruct annotation. This annotation is part of Common Annotations API and it's part of JDK module javax.

In what order do the @PostConstruct annotated method the Init-method parameter method on beans and the afterPropertiesSet () method execute?

If you have a @PostConstruct method, this will be called first before the initializing methods are called. If your bean implements InitializingBean and overrides afterPropertiesSet , first @PostConstruct is called, then the afterPropertiesSet and then init-method .

How does @PostConstruct work?

The PostConstruct annotation is used on a method that needs to be executed after dependency injection is done to perform any initialization. This method MUST be invoked before the class is put into service. This annotation MUST be supported on all classes that support dependency injection.

Which of the following annotation is equal to the Init-method attribute of the bean tag?

@Bean is a method-level annotation and a direct analog of the XML <bean/> element. The annotation supports most of the attributes offered by <bean/> , such as: init-method , destroy-method , autowiring , lazy-init , dependency-check , depends-on and scope .


1 Answers

The Spring framework is not tied into the Java programming language, it is just a framework. Therefore, in general, you need to mark a non-final field that is accessed by different threads to be volatile. At the end of the day, a Spring bean is nothing more than a Java object and all language rules apply.

final fields receive a special treatment in the Java programming language. Alexander Shipilev, The Oracle performance guy, wrote a great article on this matter. In short, when a constructor initializes a final field, the assembly for setting the field value adds an additional memory barrier that assures that the field is seen correctly by any thread.

For a non-final field, no such memory barrier is created. Thus, in general, it is perfectly possible that the @PostConstruct-annotated method initializes the field and this value is not seen by another thread, or even worse, seen when the constructor is yet only partially executed.

Does this mean that you always need to mark non-final fields as volatile?

In short, yes. If a field can be accessed by different threads, you do. Don't make the same mistake that I did when only thinking of the matter for a few seconds (thanks to Jk1 for the correction) and think in terms of your Java code's execution sequence. You might think that your Spring application context is bootstraped in a single thread. This means that the bootstraping thread will not have issues with the non-volatile field. Thus, you might think that everything is in order as long as you do not expose the application context to another thread until it is fully initialized, i.e. the annotated method is called. Thinking like this, you could assume, the other threads do not have a chance to cache the wrong field value as long as you do not alter the field after this bootstrap.

In contrast, the compiled code is allowed to reorder instructions, i.e. even if the @PostConstruct-annotated method is called before the related bean is exposed to another thread in your Java code, this happens-before relationship is not necessarily retained at in the compiled code at runtime. Thus, another thread might always read and cache the non-volatile field while it is either not yet initialized at all or even partially initialized. This can introduce subtle bugs and the Spring documentation does unfortunately not mention this caveat. Such details of the JMM are a reason why I personally prefer final fields and constructor injection.

Update: According to this answer in another question, there are scenarios where not marking the field as volatile would still produce valid results. I investigated this a little further and the Spring framework guarantees as a matter of fact a certain amount of happens-before safety out of the box. Have a look at the JLS on happens-before relationships where it clearly states:

An unlock on a monitor happens-before every subsequent lock on that monitor.

The Spring framework makes use of this. All beans are stored in a single map and Spring acquires a specific monitor each time a bean is registered or retrieved from this map. As a result, the same monitor is unlocked after registering the fully initialized bean and it is locked before retrieving the same bean from another thread. This forces this other thread to respect the happens-before relationship that is reflected by the execution order of your Java code. Thus, if you bootstrap your bean once, all threads that access the fully initialized bean will see this state as long as they access the bean in a canonical manner (i.e. explicit retrieval by querying the application context or auto-wriring). This makes for example setter injection or the use of a @PostConstruct method safe even without declaring a field volatile. As a matter of fact, you should therefore avoid volatile fields as they introduce a run time overhead for each read what can get painful when accessing a field in loops and because the keyword signals a wrong intention. (By the way, by my knowledge, the Akka framework applies a similar strategy where Akka, other than Spring, drops some lines on the problem.)

This guarantee is however only given for the retrieval of the bean after its bootstrap. If you change the non-volatile field after its bootstrap or if you leak the bean reference during its initialization, this guarantee does not longer apply.

Check out this older blog entry which describes this feature in further detail. Apparently, this feature is not documented as even the Spring people are aware of (but did not do anything about in a long time).

like image 197
Rafael Winterhalter Avatar answered Oct 07 '22 18:10

Rafael Winterhalter