Threads does not work without volatile and reads the value from RAM instead of caching

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 ?

Answer

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.

Leave a Reply

Your email address will not be published. Required fields are marked *