Question

I wanted to try the Java lang spec example myself but there is apparently something I do not understand. My understanding was that the order of incrementing of volatile counters should be the same as it appears in the code. "Surprisingly" I am getting a random counter values in terms of that one counter is sometimes less, equal and greater than the other. Is there anyone who could explain what I am missing?

The code and output below:

public class C {

private static volatile int i = 0;
private static volatile int j = 0;

static void one() {
    i++;
    j++;
}

static void two() {
    int a = i;
    int b = j;
    if(a < b)
        System.out.println(a + " < " + b);
    if(a > b)
        System.out.println(a + " > " + b);
    if(a == b)
        System.out.println(a + " = " + b);
}

public static void main(String[] args) throws Exception {
    Thread t1 = new Thread(new Runnable() {
        @Override
        public void run() {
            while(true)
                one();
        }
    });
    Thread t2 = new Thread(new Runnable() {
        @Override
        public void run() {
            while(true)
                two();
        }
    });

    t1.start();
    t2.start();

    Thread.sleep(5000);

    System.exit(0);
}
}

Output

214559700 > 214559699
214559807 > 214559806
214559917 > 214559916
214560019 = 214560019
214560137 > 214560136
214560247 = 214560247
214560349 > 214560348
214560455 = 214560455
214560561 > 214560560
214560670 = 214560670
214560776 = 214560776
214560886 > 214560885
214560995 = 214560995
214561097 < 214561098
Was it helpful?

Solution

For all three scenarios, imagine we're starting with i = 0 and j = 0... both one() and two() get to perform two operations, but the order between one and two is undefined:

a == b

two() loads i (a = 0)
two() loads j (b = 0)
one() increments i (i = 1)
one() increments j (j = 1)

a > b

one() increments i (i = 1)
two() loads i (a = 1)
two() loads j (b = 0)
one() increments j (j = 1)

a < b (rarer)

two() loads i (a = 0)
one() increments i (i = 1)
one() increments j (j = 1)
two() loads j (b = 1)

OTHER TIPS

The accesses to i and j are not synchronized, so, all the following cases can happen:

  • t2 reads i, t2 reads j, t1 writes i, t1 writes j
  • t2 reads i, t1 writes i, t2 reads j, t1 writes j
  • t1 writes i, t2 reads i, t2 reads j, t1 writes j
  • t1 writes i, t2 reads i, t2 reads j, t2 reads i, t2 reads j, t1 writes j
  • etc. etc.

When one of the threads just finished incrementing i and wants to start to increment j, the other thread possibly has set the int's a and b already, thus resulting in different values.

volatile is of different use as I think you think it does. Essentially, volatile is used to indicate that a variable's value will be modified by different threads. The volatile modifier guarantees that any thread that reads a field will see the most recently written value

Declaring a volatile Java variable means:

  • The value of this variable will never be cached thread-locally: all reads and writes will go straight to "main memory";
  • Access to the variable acts as though it is enclosed in a synchronized block, synchronized on itself.

Source and more explanation.

The use of volatile becomes more clear when you want to access the same volatile members from different threads instead of different members.

It's important to note that the threads can be interleaved in a number of ways. Since you're copying i and j into a and b, you can have modifications to i and j between copying i into a and j into b. E.g., this sort of trace would explain your output:

Some initial state, then copy

  • background: i == 214559700, j == 214559699
  • T2: a = i, b = j

    214559700 > 214559699
    

Increment 7 times, then incre— copy! —ment

  • T1: (i++ j++) (x7)
  • T1: i++
  • T2: a = i, b = j

      214559807 > 214559806
    
  • T1: j++

Increment 9 times, then incre— copy! —ment

  • T1: (i++ j++) (x9)
  • T1: i++
  • T2: a = i, b = j

    214559917 > 214559916
    
  • T1: j++

Increment 2 times, then copy

  • T1: (i++ j++) (x2)
  • T2: a = i, b = j

    214560019 = 214560019
    

If we read j first,

static void two() {
    int b = j;
    int a = i;

then it is guaranteed that a>=b.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top