Search code examples
javavolatile

Trouble understanding the semantics of volatile in Java


I've been reading up about the use of volatile variables in Java. I understand that they ensure instant visibility of their latest updates to all the threads running in the system on different cores/processors. However no atomicity of the operations that caused these updates is ensured. I see the following literature being used frequently

A write to a volatile field happens-before every read of that same field .

This is where I am a little confused. Here's a snippet of code which should help me better explain my query.

volatile int x = 0;
volatile int y = 0; 

Thread-0:                       |               Thread-1:
                                |
if (x==1) {                     |               if (y==1) {
     return false;              |                    return false; 
} else {                        |               } else {
     y=1;                       |                   x=1;
     return true;               |                   return true;
}                               |               }

Since x & y are both volatile, we have the following happens-before edges

  1. between the write of y in Thread-0 and read of y in Thread-1
  2. between the write of x in Thread-1 and read of x in Thread-0

Does this imply that, at any point of time, only one of the threads can be in its 'else' block(since a write would happen before the read)?

It may well be possible that Thread-0 starts, loads x, finds it value as 0 and right before it is about to write y in the else-block, there's a context switch to Thread-1 which loads y finds it value as 0 and thus enters the else-block too. Does volatile guard against such context switches (seems very unlikely)?


Solution

  • So I think this question is a bit in the weeds, the gist is that volatile indicates that the value of the variable may change outside the scope of the current thread, and that its value must always be read before use.

    In principle, the statement you're quoting is really saying that prior to replacing the value with the current thread, the value will be read.

    Your example is a race condition, both threads may return true, neither may return true, or they may each return a different value -- the semantics of volatile won't define execution for your example (I'd encourage you to compile and run it and see that the output varies).

    A common way to illustrate the behavior of volatile is to run two threads, where one thread updates shared state and to see what happens when the field is marked, and when it isn't:

    class VolatileTest implements Runnable
    {
            // try with and without volatile
            private volatile boolean stopRunning = false;
    
            public void triggerStop(){
                 stopRunning = true;
            }
    
            @Override
            public void run(){
                 while(!stopRunning);
                 System.out.println("Finished.");
            }
    
            public static void main (String[] args) throws java.lang.Exception
            {
                final VolatileTest test = new VolatileTest();
                new Thread(test).start();
                Thread.sleep(1000);
                test.triggerStop() = false;
            }
    }
    

    In this example, failure to mark stopRunning as volatile can lead to the while loop continuing forever, as unless stopRunning is marked as volatile it's not required to read the value on each iteration.