Happens before between threads and atomic variable

2019-07-14 23:27发布

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.

2条回答
Deceive 欺骗
2楼-- · 2019-07-15 00:03

No, they will not always be true. There is no guarantee that thread 1 will run before thread 2, or that the operations won't interleave. If they run as:

  1. thread 2 assigns t2 = 1
  2. thread 2 does its if check, which evaluates to true
  3. thread 1 assigns t1 = 2
  4. thread 2 does its assert

... then at step 3, thread 2 will see t1 != null and t2 > t1.

Thread 1 can similarly fail.

(As JB Nizet mentions, even the operations I wrote above are actually comprised of multiple operations. That level of detail isn't strictly necessary for this question specifically, but it is a good habit to get into to really break things down into their individual operations, like the incrementAndGet vs the assignment it goes into. Experience will let you filter them out a bit when you want to show why something won't work, but to show that it will work, you really do need to consider each operation.)

查看更多
别忘想泡老子
3楼-- · 2019-07-15 00:20

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
查看更多
登录 后发表回答