Java volatile example
The volatile keyword
The volatile keyword (or strictly speaking, the volatile modifier) means that a single variable is supposed to be accessed by multiple threads concurrently.
In order to achieve significant performance improvements, the Java Memory Model allows threads to cache variables in their thread local memory without the need of constantly synchronize variable values with the main memory.
If a variable is declared as volatile it means that the variable should be always written and read directly through main memory.
Let's see a simple example:
public class MyThread extends Thread { private int value = 1; @Override public void run() { System.out.print(value); try { Thread.sleep(5000); } catch (InterruptedException e) { } // Even if another thread called changeValue() // in the meantime the next print instruction is // not guaranteed to write "2" System.out.print(value); } public void changeValue() { value = 2; } }
In this example we defined a simple thread with a single private field: private int value. The field is initialized to 1. The thread class exposes a method - changeValue() - that changes the value to 2.
The thread starts executing and prints 1 to the system out. Then it will sleep for 5 seconds and finally write the value again to the console. If another thread called changeValue() in the meanwhile, for example while we were sleeping, our current thread is not guaranteed to write 2 to the console.
This is because the int value variable is not declared as volatile, so our thread doesn't know that another thread may change the variable's value. Our thread is allowed to cache the int value variable locally and then becomes completely unaware of writes originating from other threads.
In order to guarantee that writes to the int value coming from other threads are seen by our thread we may declare the variable as volatile:
public class MyThread extends Thread { private volatile int value = 1; @Override public void run() { System.out.print(value); try { Thread.sleep(5000); } catch (InterruptedException e) { } // If another thread called changeValue() // in the meantime the next print instruction is // guaranteed to write "2" System.out.print(value); } public void changeValue() { value = 2; } }
Now that we declared our variable as volatile, if another thread calls changeValue() while we are sleeping, our thread is guaranteed to write 2 to the console. This is because reads and writes of the variable are now done directly through the main memory, ie. the variable is not cached in the thread local memory.
Another important aspect of volatile is that it also establishes an happens-before relationship as we will see in the next section.
The happens-before relationship
The happens-before relationship is a very important aspect of the Java Memory Model. When an happens-before relationship is established between two distinct events it means that all changes made in the first event and also its current state will be seen and reflected in the second event.
When a thread writes into a volatile variable and another thread later accesses that same variable, an happens before relationship will be established between the two threads. All changes made by the first thread will be visible by the second thread.
This is not restricted only to the volatile variable: In fact all the other shared and locally cached variables will be propagated from the writing thread through the thread that later accessed the volatile variable, even if the other variables are not declared as volatile.
It is the same happens-before relationship that is established when two (or more) threads synchronize on an object's intrinsic lock (see Java synchronized example).
Let's see an example. Suppose a given object that is shared between multiple threads which definition is the following:
public class MyClass { private int value; public int getValue() { return value; } public void setValue(int value) { this.value = value; } }
And the following sequence of events:
Thread 2: calls setValue(2)
Thread 1: calls getValue()
Since the instance being accessed is shared between threads, even if the 3 events happen in this exact order - in a wall clock time perspective - Thread 1 is not guaranteed to read 2 in the 3rd event: getValue().
Once again this is because how the Java Memory Model is defined. Since the value variable is not volatile (and the access to the variable is also not synchronized) there is no happens-before relationship between write and read events.
Thread 1 may locally cache the variable and never read it again from main memory so it's not aware of changes made by Thread 2.
If we declare the variable as volatile then any read that follows a write will see the changes, since synchronization with main memory will occur:
public class MyClass { private volatile int value; public int getValue() { return value; } public void setValue(int value) { this.value = value; } }
Remember that the value variable and all eventually existing shared state will be synchronized with the main memory. Consequently all this synchronized state will be seen by any other thread that accesses the volatile variable from that point onwards.
Synchronization
As we have seen a volatile variable may be used to synchronize all locally cached variables a thread may hold at a given time into the main memory. Other threads which later access that same volatile variable will synchronize their local cached variables with the main memory and see the changes made by the writing thread.
So what are the differences of using a volatile variable when compared to synchronizing on an object intrinsic lock using the synchronized keyword?
- Synchronizing using a volatile variable does not allow other threads to block until a set of instructions are executed (synchronized keyword allows other threads to block until a thread is inside a synchronized section).
- A volatile variable does not allow to synchronize a whole set of instructions. It will only guarantee the happens-before relationship between distinct threads that write and access the volatile variable.
Impact in object initialization
Finally we will see how the volatile keyword may affect object initialization.
Consider the following class:
public class MyClass { private int field; public MyClass(int field) { this.field = field; } public int getField() { return field; } }
If some thread - let's call it Thread 1 - initializes a MyClass instance:
MyClass instance = new MyClass(1);
The JVM may issue the following instructions (in a simplified way):
2: Pointer to the allocated memory is saved into instance
(instance is no longer null)
3: Value for field is written
If between step 2 and 3 another thread executes the following instructions:
if(instance != null){ System.out.println(instance.getValue()); }
It may not print the expected value of 1.
One way to guarantee the expected behaviour is to declare field variable as volatile. This way we will establish the above mentioned happens-before relationship between writes and reads and the JVM will make sure that the read will occur only when the previous write has completely finished:
public class MyClass { private volatile int field; public MyClass(int field) { this.field = field; } public int getField() { return field; } }