1

Volatile is supposed to make the Threads read the values from RAM disabling thread cache, and without volatile caching will be enabled making a thread unaware of the variable change made by another thread but this does not work for the below code.

Why does this happen and code works the same with and without volatile keyword there?

public  class Racing{
    
     private  boolean won = false;  //without volatile keyword
      
     public void race() throws InterruptedException{
        Thread one = new Thread(()->{
            System.out.println("Player-1 is racing...");
            while(!won){
              won=true;
            }
            System.out.println("Player-1 has won...");
        });
        
        Thread two=new Thread(()->{
           System.out.println("Player-2 is racing...");
            while(!won){
               System.out.println("Player-2 Still Racing...");
           } 
        });
        
        one.start();
        //Thread.sleep(2000);
        two.start();
      }
      
      public static void main(String k[]) {
        Racing racing=new Racing();
        try{
             racing.race();
        }
        catch(InterruptedException ie){}
      }
  

Why does this behave the same with and without volatile ?

dreamcrash
  • 47,137
  • 25
  • 94
  • 117

2 Answers2

11

Volatile is supposed to make the threads read the values from RAM disabling thread cache

No, this is not accurate. It depends on the architecture where the code is running. The Java language standard itself does not state anything about how the volatile should or not be implemented.

From Myths Programmers Believe about CPU Caches can read:

As a computer engineer who has spent half a decade working with caches at Intel and Sun, I’ve learnt a thing or two about cache-coherency. (...) For another, if volatile variables were truly written/read from main-memory > every single time, they would be horrendously slow – main-memory references are > 200x slower than L1 cache references. In reality, volatile-reads (in Java) can > often be just as cheap as a L1 cache reference, putting to rest the notion that volatile forces reads/writes all the way to main memory. If you’ve been avoiding the use of volatiles because of performance concerns, you might have been a victim of the above misconceptions.

Unfortunately, there still are several articles online propagating this inaccuracy (i.e., that volatile forces variables to be read from main memory).

Accordingly to the language standard (§17.4):

A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable

So informally, all threads will have a view of the most updated value of that variable. There is nothing about how the hardware should enforce such constrain.

Why does this happen and code works same with and without volatile

Well (in your case) without the volatile is undefined behavior, meaning you might or not see the most updated value of the flag won, consequently, theoretically the race condition is still there. However, because you have added the following statement

System.out.println("Player-2 Still Racing...");

in:

Thread two = new Thread(()->{
             System.out.println("Player-2 is racing...");
             while(!won){
                  System.out.println("Player-2 Still Racing...");
             }
});

two things will happen, you will avoid the Spin on field problem, and second if one looks at the System.out.println code:

   public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }

one can see that there is a synchronized being called, which will increase the likelihood that the threads will be reading the most updated value of the field flag (before the called to the println method). However, even that might change based on the JVM implementation.

dreamcrash
  • 47,137
  • 25
  • 94
  • 117
1

Without volatile, there is no guarantee that another thread will see updates written to a variable. That does not mean that another thread will not see those updates if the value is not volatile. Other threads may eventually see the modified value.

In your example, you are using System.out.printlns, which contain memory barriers. That means once the println works, all variables updated before that point are visible to all the threads. The program might work differently if you do not print anything.

dreamcrash
  • 47,137
  • 25
  • 94
  • 117
Burak Serdar
  • 46,455
  • 3
  • 40
  • 59
  • Thanks for the answer and i just tried removing those print statements which keep the program alive without terminating however, when included those print statements it did terminate. I'm not sure what does memory barriers mean here ? can you elaborate further to understand ? –  Dec 28 '20 at 17:55
  • It is what causes the "thread cache" to flush to memory. It is what is usually used with volatile access, though JVM implementations may use other means to ensure the same semantics: https://en.wikipedia.org/wiki/Memory_barrier – Burak Serdar Dec 28 '20 at 18:02
  • Caches are not 'flushed' to memory. It could be that a cacheline is pushed to main memory due to limitations in the cache coherence strategy (e.g. MESI) or when the cache needs to evict a dirty cacheline. But caches as a whole are not flushed to main memory. – pveentjer Dec 29 '20 at 04:01