We will present some examples to show how to apply happens-before reasoning to check that writes are visible to subsequent reads.

Single-threaded code

As you would expect, writes are always visible to subsequent reads in a single-threaded program.

public class SingleThreadExample {
    public int a, b;
    
    public int add() {
       a = 1;         // write(a)
       b = 2;         // write(b)
       return a + b;  // read(a) followed by read(b)
    }
}

By Happens-Before Rule #1:

  1. The write(a) action happens-before the write(b) action.
  2. The write(b) action happens-before the read(a) action.
  3. The read(a) action happens-before the read(a) action.

By Happens-Before Rule #4:

  1. write(a) happens-before write(b) AND write(b) happens-before read(a) IMPLIES write(a) happens-before read(a).
  2. write(b) happens-before read(a) AND read(a) happens-before read(b) IMPLIES write(b) happens-before read(b).

Summing up:

  1. The write(a) happens-before read(a) relation means that the a + b statement is guaranteed to see the correct value of a.
  2. The write(b) happens-before read(b) relation means that the a + b statement is guaranteed to see the correct value of b.

Behavior of ‘volatile’ in an example with 2 threads

We will use the following example code to explore some implications of the Memory Model for `volatile.

public class VolatileExample {
    private volatile int a;
    private int b;         // NOT volatile
    
    public void update(int first, int second) {
       b = first;         // write(b)
       a = second;         // write-volatile(a)
    }

    public int observe() {
       return a + b;       // read-volatile(a) followed by read(b)
    }
}

First, consider the following sequence of statements involving 2 threads:

  1. A single instance of VolatileExample is created; call it ve,
  2. ve.update(1, 2) is called in one thread, and