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
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)?
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.