Approaching Volatile and Synchronized With Singleton Examples
Learn more about using volatile and synchronized keywords with these singleton examples.
Join the DZone community and get the full member experience.
Join For FreeNow, there is still lots of discussion around using volatile
and synchronized
. The wholevolatile
subject isn't very new, but to understand, it is still fundamentally important for every dedicated Java developer. Also, I could not find any comprehensive summaries on the subject that were both meaningful and selective examples, so this motivated me to invest some time into writing this post for you.
Simply put, the volatile
keyword marks a variable that always goes to the main memory, for both reads and writes, of the multiple threads accessing it (visibility feature). On the other hand, synchronized
will cause all modifications guarded by the considered lock to synchronize with main memory (visibility feature) and adds mutual exclusion; this prevents an object from being seen in an inconsistent state by one thread while some other thread is updating that object.
Singleton Using Volatile Bean Pattern
Let's understand this using a lazy loaded singleton that uses the double-checked locking idiom and implements the "volatile bean pattern." Here is the first snippet:
public class MutableSingleton {
private static volatile MutableSingleton INSTANCE;
private static final Object mutex = new Object();
private volatile boolean someFlag;
// more mutable state on this singleton
private MutableSingleton(boolean someFlag) {
this.someFlag = someFlag;
}
public static MutableSingleton getInstance() {
MutableSingleton singleton = INSTANCE;
if (singleton != null)
return singleton;
synchronized (mutex) {
if (INSTANCE == null)
INSTANCE = new MutableSingleton(false);
return INSTANCE;
}
}
public boolean isSomeFlag() {
return someFlag;
}
public void setSomeFlag(boolean someFlag) {
this.someFlag = someFlag;
}
}
That's a mutable thread-safe singleton where the INSTANCE
field is set to volatile
, and someFlag
is also volatile
but access to someFlag
is not synchronized
. Changes made to the singleton should immediately be visible to other threads (visibility feature). Here is a test case to find out:
public class MutableSingletonTest {
private long counter = 0;
public Object[] execute(Object... arguments) {
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
MutableSingleton.getInstance().setSomeFlag(true);
System.out.println("Timer interrupted main thread ...");
timer.cancel();
}
}, 1000);
while (!MutableSingleton.getInstance().isSomeFlag()) {
counter++;
};
System.out.println("Main thread was interrupted by timer ...");
return new Object[] { counter, MutableSingleton.getInstance().isSomeFlag() };
}
private class Worker implements Runnable {
@Override
public void run() {
Object[] result = execute();
System.out.println(result[0]+"/"+result[1]);
}
}
public static void main(String[] args) throws InterruptedException {
MutableSingletonTest volatileExample = new MutableSingletonTest();
Thread thread1 = new Thread(volatileExample.new Worker(), "Worker-1");
thread1.start();
Thread.sleep(5000);
}
}
Let's go through this quickly. The main()
method (line 29) creates thread1
that runs the execute()
method (line 24). The execute()
method (line 5) schedules another task for execution, which will cause a TimerThread
to be created (line 7). thread1
now simply iterates around (line 14) until someFlag
is set to true
by the timer thread (line 9). When you execute this example with the MutableSingleton
, the result should look like this:
Timer interrupted main thread ...
Main thread was interrupted by timer ...
804404197/true
The timer thread and thread1
synchronize properly, causing the volatile
keyword to be applied to the MutableSingleton
.
Making That Singleton Fail
Delete the volatile
keywords on the MutableSingleton
and run the test above again! The result should be like this (at least it was on my machine), which means thread coordination fails now:
Timer interrupted main thread ...
This suggests that changes to someFlag
are not visible to thread1
anymore. And that's, indeed, correct. The timer thread set someFlag
to true
, but since the volatile
keyword was deleted, thread1
cannot see any updates happening.
Singleton Without Volatile?
Now, let's do some other tests. What happens if we erase the volatile
keywords on the MutableSingleton
but make the accessors to someFlag
synchronized? Will the changes to the singleton state be visible? Here is such a changed MutableSingleton
class:
public class MutableSingletonSynchronized {
private static MutableSingletonSynchronized INSTANCE;
private static final Object mutex = new Object();
private boolean someFlag;
// more mutable state on this singleton
private MutableSingletonSynchronized(boolean someFlag) {
this.someFlag = someFlag;
}
public static MutableSingletonSynchronized getInstance() {
MutableSingletonSynchronized singleton = INSTANCE;
if (singleton != null)
return singleton;
synchronized (mutex) {
if (INSTANCE == null)
INSTANCE = new MutableSingletonSynchronized(false);
return INSTANCE;
}
}
public synchronized boolean isSomeFlag() {
return someFlag;
}
public synchronized void setSomeFlag(boolean someFlag) {
this.someFlag = someFlag;
}
}
In the snippet, access to someFlag
is synchronized
and I have deleted the volatile
keywords. Now, the result of the test case is like in the following console output, and notice this time without volatile
:
Timer interrupted main thread ...
Main thread was interrupted by timer ...
60113040/true
The threads seem to coordinate correctly. The reason for that is that the synchronized
blocks around the accessors to someFlag
also synchronize the thread local memory with main memory. Now, this raises the question: is volatile
actually required if we use synchronized
accessors?
The answer is an unambiguous: it depends.
A Non-Intuative Volatile Feature
volatile
on the singletons INSTANCE
field is recommended whenever the singleton contains mutable state and is created using the double-checked locking idiom. So, in our example above, the answer is: although thread coordination worked fine without volatile, it should be applied. The reason for that is that volatile adds another "feature" here, sometimes referred to as: one-time safe publication. This feature of volatile
solves an infamous issue of the double-checked locking idiom. The root cause for this issue is the following fact:
In the absence of synchronization, it is possible to see an up-to-date value for an object reference that was written by another thread and still see stale values for that object's state.
What does that mean? Without volatile
, there is a chance that the JIT compiler changes the assignment to theINSTANCE
field (line 17 in the MutableSingleton
examples). The JIT compiler may reorder the assigment, e.g. first sets the static INSTANCE
field to an instance with default values, and afterwards, it sets the mutable member fields (here someFlag
) to the values initially passed to the constructor. So, with some unlucky timing, subsequent threads may enter the first null check (outside synchronisation in line 13 of the MutableSingleton
examples), which is not null, and then, they will return that incomplete default instance to their clients. For that complex, bothersome reason, the rule comes down to:
You should use volatile on the double-checked locking idiom when creating mutable shared singletons.
Because:
An assignment to the singleton INSTANCE field won't be reordered when you make that field volatile.
Immutable Objects
Now, with immutable singletons, it's a bit different. If the singleton is an immutable object, then lazy initialization with double-checked locking should work without having to use volatile
fields. The idea is that reading and writing references to immutable objects are atomic. Therefore, with sensible care, and the sunshiny hint that you have to find out if your referenced object is really immutable:
With immutable objects applying volatile is not mandatory in the double-checked locking idiom.
Volatile Has No Mutual Exclusion
There is an addition to make which is quite important to understand when you deal with volatile. While volatile
makes the garantee that all changes to the volatile
variables are visible to all threads, it does not add the mutual exclusion feature, like synchronized
does. This means that this code example is an thread-unsafe use of volatile
:
private static volatile int nextSerialNumber = 0;
public static int generateNextSerialNumber() {
return nextSerailNumber++;
}
The change to nextSerialNumber
will be visible, but since the (++) operator is actually performing two operations (read and write), there may be data races when multiple threads access the generateNextSerialNumber()
method and access that same memory location. The snippet can be fixed by adding synchronized
like so:
private static int nextSerialNumber = 0;
public static synchronized int generateNextSerialNumber() {
return nextSerailNumber++;
}
volatile
is not required here anymore, as long the read accessor will also use synchronized
.
Combining Volatile and Synchronized
After all that has been said, a complete thread-safe, well-performing singleton must look like the following example:
public class MutableSingletonComplete {
private static volatile MutableSingletonComplete INSTANCE;
private static final Object mutex = new Object();
private volatile boolean someFlag;
private volatile int counter;
// more mutable state on this singleton
private MutableSingletonComplete(boolean someFlag) {
this.someFlag = someFlag;
}
public static MutableSingletonComplete getInstance() {
MutableSingletonComplete singleton = INSTANCE;
if (singleton != null)
return singleton;
synchronized (mutex) {
if (INSTANCE == null)
INSTANCE = new MutableSingletonComplete(false);
return INSTANCE;
}
}
public boolean isSomeFlag() {
return someFlag;
}
public void setSomeFlag(boolean someFlag) {
this.someFlag = someFlag;
}
public int getCounter() {
return counter;
}
public synchronized void incrementCounter() {
counter++;
}
}
To summarize, for this:
You need
volatile
on theINSTANCE
field (apply one-time publication feature).You need
volatile
on the member fields of the singleton itself (apply visibility feature).If you do more then an atomic action on mutable state, you will need to use
synchronized
, even though the field is declaredvolatile
(apply mutual exclusion feature).To make this complete: if the
volatile
member field in your singleton happens to be an object reference, either the object implements the same thread-safe features like I just described, or it needs to be (at least "effectively") immutable.
OK, that's really enough confusion today for me. Let's go get some coffein. The code of the examples can be found here.
I hope the examples helped a little to further systemize the volatile
subject. Let's discuss this if you would like to find out if I missed something, which can happen quickly on such complex topics. Looking forward to your comments, questions, and thoughts!
Opinions expressed by DZone contributors are their own.
Comments