As I understand it, volatile
helps in memory visibility and synchronized
helps in achieving execution control. Volatile
just guarantees that the value read by the thread would have the latest value written to it.
Consider the following:
public class Singleton{
private static volatile Singleton INSTANCE = null;
private Singleton(){}
public static Singleton getInstance(){
if(INSTANCE==null){
synchronized(Integer.class){
if(INSTANCE==null){
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
In the above piece of code, we use double-checked locking. This helps us create only one instance of Singleton and this is communicated
to the other threads by the creating thread as soon as possible. This is what the keyword volatile
does. We need the above synchronized block
because the delay in the thread reading the INSTANCE
variable as null and initializing the object could cause a race condition
.
Now consider the following:
public class Singleton{
private static Singleton INSTANCE = null;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(INSTANCE==null){
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
Say we have 2 threads t1
and t2
trying to get the Singleton
object. Thread t1
enters the getInstance()
method first and creates the INSTANCE
object. Now this newly created object should be visible to all the other threads. If the INSTANCE
variable is not volatile
then how do we make sure that the object is still not in t1's
memory and visible to other threads. How soon is the above INSTANCE
initialized by t1
visible to other threads ?
Does this mean that it is advisable to always make variables volatile with synchronized ? In what scenarios would we not require the variable to be volatile ?
P.S I have read other questions on StackOverflow but could not find the answer to my question. Please comment before down-voting.
My question arises from the explanation given here
I think what you're missing is this from JLS 17.4.4:
An unlock action on monitor m synchronizes-with all subsequent lock actions on m (where "subsequent" is defined according to the synchronization order).
Which is very similar to the bullet about volatile variables:
A write to a volatile variable v (ยง8.3.1.4) synchronizes-with all subsequent reads of v by any thread (where "subsequent" is defined according to the synchronization order).
Then in 17.4.5:
If an action x synchronizes-with a following action y, then we also have hb(x, y).
... where hb is the "happens-before" relation.
Then:
If one action happens-before another, then the first is visible to and ordered before the second.
The memory model is incredibly complicated and I don't claim to be an expert, but my understanding is that the implication of the quoted parts is that the second pattern you've shown is safe without the variable being volatile - and indeed any variable which is only modified and read within synchronization blocks for the same monitor is safe without being volatile. The more interesting aspect (to me) is what happens to the variables within the object that the variable's value refers to. If Singleton
isn't immutable, you've still potentially got problems there - but that's one step removed.
To put it more concretely, if two threads call getInstance()
when INSTANCE
is null, one of those threads will lock the monitor first. The write action of a non-null reference to INSTANCE
happens-before the unlock operation, and that unlock operation happens-before the lock operation of the other thread. The lock operation happens-before the read of the INSTANCE
variable, therefore the write happens-before the read... at which point, we are guaranteed that the write is visible to the reading thread.
See more on this question at Stackoverflow