I've got the question about happens-before mechanism in Java. Here is the example:
public class MyThread extends Thread {
int a = 0;
volatile int b = 0;
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
read();
}
public void write(int a, int b) {
this.a = a;
this.b = b;
}
public void read() {
System.out.println(a + " " + b);
}
}
This is clear example of happens-before and this is safe enough, as I know. a
will get new value properly, because it stands before b
's initialization. But does reordering mean that safety is not guaranteed? I'm talking about this:
public void write(int a, int b) {
this.b = b;
this.a = a;//might not work? isn't it?
}
UPD Main thread:
public class Main {
public static void main(String[] args) throws InterruptedException {
MyThread t = new MyThread();
t.start();
t.write(1, 2);
}
}
Making b
volatile
and having write
set a value to b
after it sets a value to a
guarantees that:
b
is read before a
, thena
's value will not be older than the value it had when b
was previously written to.Namely, the write to b
and the read from b
act as a synchronization point, or a fence, which means whatever was written to a
before the write to b
will be readable after the read from b
(if a
is written to more than once, the following writes might or might not be visible).
However, your code is not safe as it is now. In read
, you have the following expression:
a + " " + b
In Java, expressions are evaluated left-to-right. Which means, a
will be evaluated before b
. Consequently, it is completely possible that the read of a
will see an old value of a
, while the read of b
will see a new value of b
. Had you had b
read before a
, the happen-before relation between b
's write and read would have covered the write to a
and the read from a
. Now, it doesn't.
As for reordering operations in write
- that will clearly eliminate the effect b
's ordering guarantees have over operations on a
, even if read
first reads b
and then reads a
.
To visualize things, given only b
is volatile
:
The current code does not guarantee ordering of operations on a -
write a read a
write b --> happens before --> read b
Reordering write
using the given read will not order a
as well -
read a
write b --> happens before --> read b
write a
And even if the given read
is fixed, a
will not be ordered by b
-
write b --> happens before --> read b
write a read a
The only safe thing to do is keeping the current write
, and fixing the read
-
write a
write b --> happens before --> read b
read a