Search code examples
javaconcurrencyatomicvolatilejava.util.concurrent

Happens before between threads and atomic variable


Suppose an AtomicInteger,c, is shared between two threads, thread1 and thread2. Thread1 sets (one time only) a volatile variable t1 using c.incrementAndGet(). Thread2 sets (one time only) a volatile variable t2 using c.incrementAndGet(). Once t1 and t2 are set they are not set again by any other thread. Suppose that after thread1 sets t1, it checks the value of t2 and gets null. Is it guaranteed that t2 is subsequently set to a higher value then t1? (and visa versa). In other words are the assert below always true? Is so why?

AtomicInteger c = new AtomicInteger();
volatile Integer t1=null;
volatile Integer t2=null;

//Thread1
t1=c.incrementAndGet();
if(t2 == null){
  assert t2==null || t2>t1;
}

//Thread2
t2=c.incrementAndGet();
if(t1==null){
  assert t1==null || t1>t2;
}

I believe the asserts are true for the following reason: If t1 is assigned a value from incrementing c and t2 has not yet been assigned a value by incrementing c then when t2 is subsequently assigned a value by incrementing c, it must be greater then the value of t1.

Update: Since as per the correct answer below the asserts may not always hold, I have added a part 2 question: Check out Happens before between threads and atomic variable Part 2.


Solution

  • No, there's no guarantee. The following could happen:

    • thread2: c.incrementAndGet (c is 1, and t2 is still null, but will be initialized with 1 later)
    • thread1: c.incrementAndGet (c is 2, and t1 is still null, but will be initialized with 2 later)
    • thread1: t1 = 2
    • thread1: if (t2 == null): the condition is true. The if block is evaluated
    • thread2: t2 = 1
    • thread1: t2 == null: the condition is false, so the other operand of the or is evaluated
    • thread1: t2 > t1: false since t2 is 1 and t1 is 2
    • thread1: assert: fails