问题
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.
回答1:
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
回答2:
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:
- thread 2 assigns
t2 = 1
- thread 2 does its
if
check, which evaluates to true - thread 1 assigns
t1 = 2
- 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.)
来源:https://stackoverflow.com/questions/53363062/happens-before-between-threads-and-atomic-variable