0

Please refer to the following codes.

Shared.java

class Shared
{
    public volatile int i = 0;
}

CreateThread.java

class CreateThread
{
    public static void main(String[] args) throws InterruptedException
    {
        Shared s = new Shared();
        MyThread t1 = new MyThread(s);
        t1.start();
        while(s.i!=7)
            System.out.println(s.i);
    }
}

MyThread.java

class MyThread extends Thread
{
    Shared s = null;
    MyThread(Shared s)
    {
        this.s = s;
    }   
    public void run()
    {
        for(int j=0; j<15; j++)
        {
    /*      try
            {
                Thread.sleep(1000);
            }
            catch(Exception e){}*/
            s.i = s.i+1;
        //  System.out.println("New Value of s.i "+s.i);
    /*      try
            {
                Thread.sleep(1000);
            }
            catch(Exception e){}*/
        }
        System.out.println("Thread Going to Stop");
    }
}

If the new thread is not allowed to sleep, then it seems that the main thread cannot find all the values of the variable s.i. Because in this case, we are getting the following output.

0
15
15
15
..
..
..

If the new thread is allowed to sleep, then it seems that the main thread can find all the values of the variable s.i. Because in this case, we are getting the following output.

0
0
..
1
1
..
2
2
..
3
3
..
4
4
..
upto 6

From the above output, it is clear that if the new thread does not go into the sleep state, the new thread is changing the value of s.i in memory several times before the thread main gets a chance to read it.

If I change the program as:

Shared.java

class Shared
{
    public /*volatile*/ boolean i = false;
}

MyThread.java

class MyThread extends Thread
{
    Shared s = null;
    MyThread(Shared s)
    {
        this.s = s;
    }   
    public void run()
    {
    
            s.i = true;
            System.out.println("New Value of s.i "+s.i);
            try
            {
                Thread.sleep(10000);
            }
            catch(Exception e){}
        System.out.println("Thread Going to Stop");
    }
}

CreateThread.java

class CreateThread
{
    public static void main(String[] args) throws InterruptedException
    {
        Shared s = new Shared();
        MyThread t1 = new MyThread(s);
        t1.start();
        while(s.i==false)
            System.out.println(s.i);
        System.out.println(s.i+" Main Stopped");
    }
}

Output:

C:\Users\gyan0\OneDrive\Desktop>java CreateThread
false
New Value of s.i true
true Main Stopped
Thread Going to Stop

It seems that the data, even though it is not volatile, is becoming immediately available to the thread.

Following are my questions.

  1. Does the volatile keyword guarantee the latest value to be read by a reader thread?
  2. What the benefits we would get if volatile keyword saves a data in memory by not saving it in CPU Register? Can you give a practical working example proving the benefits of the volatile?
my name is GYAN
  • 1,269
  • 13
  • 27
  • 1
    The `volatile` keyword creates a [_happens-before_ relationship](https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/concurrent/package-summary.html#MemoryVisibility). – Slaw Aug 21 '20 at 07:00
  • And: [What is the relationship between Thread.sleep and happens-before?](https://stackoverflow.com/questions/42417636/what-is-the-relationship-between-thread-sleep-and-happens-before) (see [John Vint's answer](https://stackoverflow.com/a/42423344/6395627)). – Slaw Aug 21 '20 at 07:06

2 Answers2

1

The volatile variable creates a happens before edge between a read and a subsequent write. So the read will see the most recent write before it in the synchronization order.

To give you a counter example. Imagine i would not be volatile:

while(s.i==false)
        System.out.println(s.i);

Because s.i isn't changed in the loop, the compiler could hoist the read of i out of the loop like this.

boolean r = s1.i;
while(r == false)
        System.out.println(r);

And now the read will never see the written value in the loop.

This code could even be further optimized:

boolean r = s1.i;
if(r==false){
    while(true)
        System.out.println(false);
}
Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
pveentjer
  • 10,545
  • 3
  • 23
  • 40
0

Regarding your question 2, all the threads have its local cache which may not be immediately updated to/from main memory. Using volatile, it ensures that the value is updated and threads can get the updates values. you can check this below example:

https://dzone.com/articles/java-multi-threading-volatile-variables-happens-be-1

Suvendu Ghosh
  • 391
  • 1
  • 3
  • 16
  • If you run on X86, then all caches are always coherent. It will never happen that a cache is out of date. However main memory doesn't need to be correct; it could be that changes within the cache are not pushed yet/ever to main memory. In short: the cache is the source of truth, main memory is not. – pveentjer Aug 21 '20 at 12:31
  • StoreLoad barriers (despite the exact CPU instructions used to implement them) are put after Volatile writes, to make them visible to subsequent Volatile Reads in other threads (since x86 doesn't guarantee that newer reads always see older writes) - https://stackoverflow.com/questions/36811405/java-volatile-and-memory-barriers-on-x86-architecture – Suvendu Ghosh Aug 21 '20 at 14:34
  • 1
    This is not correct. StoreLoad barrier prevents older stores from being reordered with newer loads to a different address (hence the name StoreLoad). This reordering is caused by the existence of Store buffers on CPUs. Caches are always coherence; when the write wants to enter the L1D, first the cache line needs to be invalidated on all other CPUs using a RFO and only after that the write can commit to the L1D. The StoreLoad barrier doesn't have any influence on visibility of a store on other CPUs. For more info check https://shipilev.net/blog/2014/on-the-fence-with-dependencies/ – pveentjer Aug 21 '20 at 15:31