Monday, March 24, 2008

How 'volatile' are they?

What are volatile variables?

A variable in Java if declared volatile ensures the visibilty of the variable when shared across different threads. Lets consider a variable, say an int i and this variable is declared volatile. Now if Thread 1 changes the value of i from1 to 10 at time t = 0. At time t=1 the value of i which thread 2 reads is 10 and not 1. This is bacause for volatile variables the values which is read is the shared value, which is the most recent value of the variable.

Are they as powerful as locks?

So if the visibility of a shared variable is guaranteed, can one do away with locking with volatile variables?

Consider the following:

A Runnable Class:

public class VolatileRunnable implements Runnable{

private volatile int i;// This is a shared variable

public void run() {

for (int j = 0; j < 10000; j++) {
i++;
}
}

public int getI() {
return i;
}
}



And a main method which uses the Runnable:

public static void main(String[] args) throws InterruptedException {
VolatileRunnable r = new VolatileRunnable();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);

t1.start();
t2.start();
t1.join();
t2.join();

System.out.println("r.getI() = " + r.getI());
}


Now since the variable i is shared and declared volatile what would be the expected output? To me, going by the definition of volatile the output would be (number of threads) * 10000 (20000 in this case). On running the program I saw the output of 20000 was not always true. There were times when the output was 19445, 19667 etc.

Why is that even if the variable is declared volatile?

The operation i++ is actually a series of read, use and write operations on the variable i. Each of these individual operations are atomic but the combination of them is not.

A snapshot of time

t1 Thread 1 read (i = 3)
t2 Thread 2 read (i = 3)
t3 Thread 1 used (i + 1 = 4)
t4 Thread 2 used (i + 1 = 4)
t5 Thread 1 wrote (i = 4)
t6 Thread 2 wrote (i = 4)


This is a case of lost update. This is what would have happened when the above example was run. Some of the increments were lost and hence the non deterministic output.

Hence in this case even though the variable was volatile atomicity was not guaranteed and so synchronization is indispensable to ensure the expected outcome.

The moral:

Volatile variables are not enough when the expected behaviour in any piece of code depends on the value of a variable before any write operation.

1 comments:

jitesh said...

Nice post neha..
But honestly if i have to think that i++ is actually two read, one arithmetic operation, and one write.. probably i can't code.
So I enabled the idea warning Settings->Errors(7)->Threading issues->Arithmetic operation on volatile field. I configured it as error.
But unfortunately in idea 6 it works only when i do i=i+1; and not i++..still it was quite helpful.