Search code examples
javamultithreadingshared-memorymemory-barriers

volatile vs not volatile


Let's consider the following piece of code in Java

int x = 0;
int who = 1
Thread #1:
   (1) x++;
   (2) who = 2;

Thread #2
   while(who == 1);
   x++;   
   print x; ( the value should be equal to 2 but, perhaps, it is not* )    

(I don't know Java memory models- let assume that it is strong memory model- I mean: (1) and (2) will be doesn't swapped)
Java memory model guarantees that access/store to the 32 bit variables is atomic so our program is safe. But, nevertheless we should use a attribute volatile because *. The value of x may be equal to 1 because x can be kept in register when Thread#2 read it. To resolve it we should make the x variable volatile. It is clear.

But, what about that situation:

    int x = 0;
    mutex m; ( just any mutex)
Thread #1:
       mutex.lock()
       x++;
       mutex.unlock()

    Thread #2
       mutex.lock()
       x++;   
       print x; // the value is always 2, why**?
       mutex.unlock()

The value of x is always 2 though we don't make it volatile. Do I correctly understand that locking/unlocking mutex is connected with inserting memory barriers?


Solution

  • I'll try to tackle this. The Java memory model is kind of involved and hard to contain in a single StackOverflow post. Please refer to Brian Goetz's Java Concurrency in Practice for the full story.

    The value of x is always 2 though we don't make it volatile. Do I correctly understand that locking/unlocking mutex is connected with inserting memory barriers?

    First if you want to understand the Java memory model, it's always Chapter 17 of the spec you want to read through.

    That spec says:

    An unlock on a monitor happens-before every subsequent lock on that monitor.

    So yes, there's a memory visibility event at the unlock of your monitor. (I assume by "mutex" you mean monitor. Most of the locks and other classes in the java.utils.concurrent package also have happens-before semantics, check the documentation.)

    Happens-before is what Java means when it guarantees not just that the events are ordered, but also that memory visibility is guaranteed.

    We say that a read r of a variable v is allowed to observe a write w
    to v if, in the happens-before partial order of the execution trace:
    
        r is not ordered before w (i.e., it is not the case that 
        hb(r, w)), and
    
        there is no intervening write w' to v (i.e. no write w' to v such
        that hb(w, w') and hb(w', r)).
    
    Informally, a read r is allowed to see the result of a write w if there
    is no happens-before ordering to prevent that read. 
    

    This is all from 17.4.5. It's a little confusing to read through, but the info is all there if you do read through it.