问题
Does Java allows output 1, 0
? I've tested it very intensively and I cannot get that output. I get only 1, 1
or 0, 0
or 0, 1
.
public class Main {
private int x;
private volatile int g;
// Executed by thread #1
public void actor1(){
x = 1;
g = 1;
}
// Executed by thread #2
public void actor2(){
put_on_screen_without_sync(g);
put_on_screen_without_sync(x);
}
}
Why?
On my eye it is possible to get 1, 0
. My reasoning.
g
is volatile so it causes that memory order will be ensured. So, it looks like:
actor1:
(1) store(x, 1)
(2) store(g, 1)
(3) memory_barrier // on x86
and, I see the following situation:
reorder store(g, 1)
before store(x,1)
(memory_barrier is after (2)).
Now, run thread #2. So, g = 1, x = 0
. Now, we have expected output.
What is incorrect in my reasoning?
回答1:
Any actions before a volatile write happen before (HB) any subsequent volatile read of the same variable. In your case, the write to x
happens before the write to g
(due to program order).
So there are only three possibilities:
- actor2 runs first and x and g are 0 - output is 0,0
- actor1 runs first and x and g are 1 because of the happens before relationship HB - output is 1,1
- the methods run concurrently and only
x=1
is executed (notg=1
) and the output could be either 0,1 or 0,0 (no volatile write so no guarantee)
回答2:
No, this isn't possible. According to the JMM, anything that was visible to thread 1 when it writes to a volatile field becomes visible to thread 2 when it reads that field.
There is another example similar to yours provided here:
class VolatileExample {
int x = 0;
volatile boolean v = false;
public void writer() {
x = 42;
v = true;
}
public void reader() {
if (v == true) {
//uses x - guaranteed to see 42.
}
}
}
回答3:
No, and in fact this property of volatile
is used in classes like ConcurrentHashMap
to implement a lock-free happy path, roughly like this:
volatile int locked = 0;
...
void mutate() {
if (Unsafe.compareAndSwapInt(locked,0,1)) {
/*this isn't exactly how you call this method, but the point stands:
if we read 0, we atomically replace it with 1 and continue on the happy
path */
//we are happy
//so we mutate the structure and then
locked = 0;
} else {
//contended lock, we aren't happy
}
}
Since writes before a volatile write can't be reordered after the volatile write, and reads after volatile read can't be reordered before the volatile read, code like this indeed works as a "lockless locking".
来源:https://stackoverflow.com/questions/45133832/deep-understanding-of-volatile-in-java