You have to reason in terms of happens-before
when thinking about multi-threaded code in java, this is what the JLS
uses and this is what you should use. Period.
Going to your example, you munch volatile
and synchronized
together as if they do the same thing, sort of - they don't. Even your example is broken, for the "other" thread to see a guaranteed a = 5
it must synchronize on the same lock, which you don't. A jcstress test proves you wrong (I'll let you figure out how to run this exactly)
@JCStressTest
@State
@Outcome(id = "1, 0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "SURPRISE")
@Outcome(id = "0, 1", expect = Expect.ACCEPTABLE, desc = "whatever")
@Outcome(id = "0, 0", expect = Expect.ACCEPTABLE, desc = "whatever")
@Outcome(id = "1, 1", expect = Expect.ACCEPTABLE, desc = "whatever")
public class DifferentSynchronizedObjects {
int x, y;
private Object lock = new Object();
@Actor
public void actor1() {
synchronized (lock) {
x = 1;
y = 1;
}
}
@Actor
public void actor2(II_Result r) {
r.r1 = x;
r.r2 = y;
}
}
Even if you do not understand the code, it's main "selling point" is this:
@Outcome(id = "1, 0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "SURPRISE")
You can read it as : "while you were in the synchronized(lock){....}, some other thread came and read x and y". That reading thread saw 1, 0
(x = 1
, y = 0
) and now think about it. You were in the synchronized
block, how come some thread read x = 1
and y = 0
, aren't you "protected"? No you are not. If you run this - you will get 1, 0
.
EDIT to answer the comment
You think you are protecting both writes to x
and y
- but since the JLS
makes no such guarantees, it's your understanding of things, which is wrong. As simple as that. The only protection you actually get is if your writer and reader would use the same lock.
An optimizer might "see" that you are using that lock
only inside a method, and as such transform that code to (in theory at least):
@Actor
public void actor1() {
Object lock = new Object(); // < -- make lock local
synchronized (lock) {
x = 1;
y = 1;
}
}
Since the lock
is now local to a method what is the point in having it at all? No one can access it, and simply elide it entirely. Thus you get a totally unprotected code that performs two independent writes.
The conclusion is : you do not follow the rules that the JLS
gives you - be prepared to get weird results.