Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Volatile is behaving weirdly

I have experience this weird behavior of volatile keyword recently. As far as i know,

  1. volatile keyword is applied on to the variable to reflect the changes done on the data of the variable by one thread onto the other thread.

  2. volatile keyword prevents caching of the data on the thread.

I did a small test........

  1. I used an integer variable named count, and used volatile keyword on it.

  2. Then made 2 different threads to increment the variable value to 10000, so the end resultant should be 20000.

  3. But thats not the case always, with volatile keyword i am getting not getting 20000 consistently, but 18534, 15000, etc.... and sometimes 20000.

  4. But while i used synchronized keyword, it just worked fine, why....??

Can anyone please explain me this behaviour of volatile keyword.

i am posting my code with volatile keyword and as well as the one with synchronzied keyword.

The following code below behaves inconsistently with volatile keyword on variable count

public class SynVsVol implements Runnable{

    volatile int  count = 0;

    public void go(){

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

    @Override
    public void run() {
        go();
    }

    public static void main(String[] args){

        SynVsVol s = new SynVsVol();
        Thread t1 = new Thread(s);
        Thread t2 = new Thread(s);
        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("Total Count Value: "+s.count);
    }
}

The following code behaves perfectly with synchronized keyword on the method go().

public class SynVsVol implements Runnable{

    int  count = 0;

    public synchronized void go(){
        for (int i=0 ; i<10000 ; i++){
             count = count + 1;
         }
    }

    @Override
    public void run() {
        go();
    }

    public static void main(String[] args){

        SynVsVol s = new SynVsVol();
        Thread t1 = new Thread(s);
        Thread t2 = new Thread(s);
        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("Total Count Value: "+s.count);
    }
}
like image 514
Kumar Vivek Mitra Avatar asked Jul 01 '12 10:07

Kumar Vivek Mitra


1 Answers

count = count + 1 is not atomic. It has three steps:

  1. read the current value of the variable
  2. increment the value
  3. write the new value back to the variable

These three steps are getting interwoven, resulting in different execution paths, resulting in an incorrect value. Use AtomicInteger.incrementAndGet() instead if you want to avoid the synchronized keyword.

So although the volatile keyword acts pretty much as you described it, that only applies to each seperate operation, not to all three operations collectively.

like image 147
Adam Avatar answered Oct 15 '22 18:10

Adam