One of the least understood aspects of Java Thread model is the "volatile" keyword. The general misconception is that if you make a variable volatile you don't need to provide synchronized access to it.
Let's create a simple class with a integer marked as volatile. There are two methods which increment and decrement the variable in a for loop. To increase interleaving of the threads, we have a call to yield inside the for loop.
Code snippet 1
- public class VolatileExample {
- private volatile int volatileInt=0;
- /** unsynchronized volatile variable being incremented */
- public int incrementVolatileInt(int count)
- {
- for(int i=0; i <count; i++){
- volatileInt++;
- Thread.yield();
- }
- return volatileInt;
- }
- /** unsynchronized volatile variable being decremented */
- public int decrementVolatileInt(int count)
- {
- for(int i=0; i <count; i++){
- volatileInt--;
- Thread.yield();
- }
- return volatileInt;
- }
- public int getVolatileInt() {
- return volatileInt;
- }
- }
Do we expect the increment and decrement operations in line 9 and line 18 to be atomic? If they are atomic, incrementing and decrementing these variable equal number of times, in several threads should result in the final value being zero.
Code Snippet 2
- public static void main(String [] args) throws InterruptedException
- {
- final VolatileExample volatileExample=new VolatileExample();
- ThreadPoolExecutor pool= new ThreadPoolExecutor(4, 4, 1, TimeUnit.NANOSECONDS, new ArrayBlockingQueue<Runnable>(200));
- final int operationCount=10; final int incrementCount=100000;
- for (int i=0; i <operationCount; i++){
- pool.execute(new Runnable() { public void run() { volatileExample.incrementVolatileInt(incrementCount);} });
- pool.execute(new Runnable() { public void run() { volatileExample.decrementVolatileInt(incrementCount);} });
- }
- pool.shutdown();
- System.out.println("All Threads Shutdown: " +pool.awaitTermination(5, TimeUnit.SECONDS));
- //Both volatile and regular variable should return zero, if both methods are equally accurate.
- System.out.println("Volatile Int:"+volatileExample.getVolatileInt());
- }
We are calling the increment and decrement methods with equal values in 10 different threads. When we run this main method, we will almost never see the value of the volatile variable to be zero.
Now let's add another regular variable to the class and provide similar increment and decrement methods for this regular variable and call them from main(). But this time all methods are synchronized.
- public synchronized int incrementRegularInt(int count)
- {
- for(int i=0; i <count; i++){
- regularInt++;
- Thread.yield();//to ensure threads interleave
- }
- return regularInt;
- }
- public synchronized int decrementRegularInt(int count)
- {
- for(int i=0; i <count; i++){
- regularInt--;
- Thread.yield();
- }
- return regularInt;
- }
- public synchronized int getRegularInt() {
- return regularInt;
- }
- for (int i=0; i <operationCount; i++){
- pool.execute(new Runnable() { public void run() { volatileExample.incrementRegularInt(incrementCount);} });
- pool.execute(new Runnable() { public void run() { volatileExample.decrementRegularInt(incrementCount);} });
- System.out.println("Regular Int:"+ volatileExample.getRegularInt());
The value of the regular int is always zero. If you convert the increment and decrement methods for the volatile int to synchronized, you will see similar results.
So the conclusion is, volatile variables should be synchronized. Let's examine, why.
The synchronized keyword has two functionality. We are all familiar with the first, it provides atomic access to the synchronize block guarded by a lock. In addition to this, JVM also guarantees that all threads will see the latest updated value of this variable. In other words if thread T1 has updated the variable at time x, and another thread T2 reads the variable at time y where x is earlier than y on the time axis, then it will always see the value updated by T1.
The volatile keyword provides the second feature, but not atomic access. In our case when we had a "read-modify-write" operation ( volatileInt=volatileInt+1) in line 9 and 18 of code snippet 1, it had to be made synchronized to make it thread safe.
"volatile is not a substitution for synchronized".
When should we use volatile variable? When your modifications to primitive values are just getting and setting the values, it's cheaper to use volatile variable rather than synchronized blocks.
Code Snippet 3
- for (int i=0; i <=100; i++){
- final int value=i;
- //calling setting of a volatile variable in a MT program works fine...
- pool2.execute(new Runnable() { public void run() { volatileExample.setVolatileInt(value);} });
- }
- pool2.shutdown(); pool2.awaitTermination(2, TimeUnit.SECONDS);
- System.out.println("Volatile Int after simple setter invocation:"+volatileExample.getVolatileInt());
In code snippet 3, we are setting the value of the volatile variable to different values and the last value is 100. You will always see 100 when you check the value later on in the code.
P.S.
The code sample is attached.
References
Java Concurrency in Practice by Brian Goetz













Use AtomicLong or AtomicInteger whenver dealing with arithmetic operations on numeric types , instead of volatile.
Use AtomicLong or AtomicInteger whenver dealing with arithmetic operations on numeric types , instead of volatile.
Regards,
Pavan Kumar
http://guibuilder.wordpress.com/
Post new comment