2

I need some example on Volatile Keyword of Java Threads.

As per definition of volatile keyword it says, when variable is declared as volatile then thread will directly read/write to variable memory instead of read/write from local thread cache.

please correct me if I am wrong.

So in that understanding when I run the below program,

public class ThreadRunnableBoth implements Runnable{  
    private volatile int num =0;  

    public void run(){  
        Thread t = Thread.currentThread();  
        String name = t.getName();

        for(int i=0; i<100; i++){
            if(name.equals("Thread1")){
                num=10;
                System.out.println("value of num 1 is :"+num);
            }else{
                num=15;
                System.out.println("value of num 2 is :"+num);  
            }  
        }

    }  
    public static void main(String args[]) throws InterruptedException{  
        Runnable r = new ThreadRunnableBoth();  
        Thread t1 = new Thread(r);  
        t1.setName("Thread1");

        Thread t2 = new Thread(r);  
        t2.setName("Thread2");  

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

    }  
}  

I got these example from some site and when i tried running it I cant see any difference removing Volatile or adding Volatile Keyword.

Please explain me the difference happens on removing it and adding it.

Thanks a lot.

Jayesh
  • 6,047
  • 13
  • 49
  • 81

6 Answers6

3

The main differences between having a volatile keyword or not is whether you need a memory fence to safely operate with the data.

Memory fences prevent side effects that can occur amongst multiple threads due to out-of-order execution. By instructing the CPU, the compiler / runtime environment can tell the CPU that the original ordering constraint on the read cannot be manipulated without destroying the correctness of the program.

Read up on memory fences here, and remember that the key to the solution is consistency, not location. The read request can stop at cache, provide that the cache is guaranteed to be consistent (by the CPU's internal mechanisms).

Edwin Buck
  • 69,361
  • 7
  • 100
  • 138
0

So without the volatile keyword, the threads are just printing the value of num in their local memory cache. Their changes to num are in no way synchronized with the other thread's view of num. I see output like:

value of num 1 is :10
value of num 2 is :15
value of num 1 is :10
value of num 2 is :15
value of num 1 is :10
value of num 2 is :15
...

With volatile, they are both updating and printing to the same global storage location with memory barriers around the set/get. But this won't change the output which is very subject to race conditions. I see output like:

value of num 2 is :15
value of num 1 is :15
value of num 2 is :15
value of num 1 is :10
value of num 2 is :15
value of num 1 is :10
...

There is a race between which set was last when the value is printed.

You may not be seeing this output because your processor architecture or JRE is context switching only on the IO events or otherwise not providing a full threaded execution. If you show some output then I can comment some more.

Gray
  • 115,027
  • 24
  • 293
  • 354
0

As per definition of volatile keyword it says, when variable is declared as volatile then thread will directly read/write to variable memory instead of read/write from local thread cache.

Not necessarily. A system that supports cache coherence can have volatile fields up to date without ever reading from main-memory. Volatile says that each thread will see the most up-to-date value of a certain field.

As for memory visibility you won't necessarily see any changes (immediately) if you remove volatile but your program is suspect to failure. The longer it runs the more problems you may end up seeing.

John Vint
  • 39,695
  • 7
  • 78
  • 108
0

The effect of volatile variable is evident on multiprocessor system wherein different threads run on different processors. On ordinary single processor system, the impact may not be evident.

Here is good discussion thread on this site on the same subject.

Community
  • 1
  • 1
Santosh
  • 17,667
  • 4
  • 54
  • 79
0

In your example, num starts with a default value of 0, and you then (on its declaration line) assign it to 0. That assignment would be a data race if num weren't volatile, but of course you wouldn't be able to tell the difference.

Then you only use num in one thread, and within a thread you will always see things happen in the order the code said they would. So in this case, num doesn't have to be volatile.

Now, if you modified your main method so that it checked t1.num after the thread had started (but without checking that it has finished in a way that creates a happens-before edge, like Thread.join), you would have a data race without num being volatile. You could have main wait for 5 days, and it still wouldn't be guaranteed to see num as anything other than 0. And not just that, but if ThreadRunnableBoth also had a non-volatile boolean that started false and were set to true at the end of the leap, main could also see that boolean as true (thus meaning the thread had finished) but num still at 0! This is a data race, and can happen (for instance) on a multicore machine where the boolean is flushed out of a local register before num is. In this example, making both num and the boolean volatile will ensure that if the boolean is true, num == 0 || num == 15.

But here's the kicker: even without the volatile keyword -- that is, even in the presence of a data race -- you're not guaranteed to see racy behavior. That is, the data race says you can't guarantee that you'll see the change in another thread -- but it doesn't guarantee that you won't. It could be that it works just fine 100 times on your machine, and then someone puts it on an 8-core machine in the wild, and it's part of a more complex program so it gets optimized differently, and then things break.

yshavit
  • 42,327
  • 7
  • 87
  • 124
0

Most of the talk is about hardware. Actually compiler optimisations are typically more relevant. You're accessing a field repetitively in a small method, so let's put it in a register. Altering the physical memory wont alter the value in the register.

Although the ("new", but many years old) Java Memory Model (JMM) does not talk about main memory like the old one and does not provide guarantees of progress (very difficult to actually specify), implementation of the volatile/happens-before specification will result in eviction from the register and synchronisation between threads.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305