I've been reading up on Java concurrency and had forgot the fact that synchronization blocks in two threads using the same lock also affect the visibility of variables, even though they were not defined as "volatile". If I have code like this
Object lock = new Object();
boolean a = false, b = false, c = false;
void threadOne() {
a = true;
synchronized(lock) {
b = true;
}
c = true;
}
void threadTwo() {
while (true) {
synchronized(lock) {
if (a && b && c) break;
}
}
}
... and threadOne and threadTwo will be called by different threads:
Is it guaranteed that the code will break out of the while loop?
What if we remove variable c out of the equation? I'm wondering if only b is guaranteed to be visible in threadTwo because it was inside the synchronization block.
Is it guaranteed that the code will break out of the while loop?
No. The Java memory model is defined in terms of "happens before" relationships:
Two actions can be ordered by a happens-before relationship. If one action happens-before another, then the first is visible to and ordered before the second.
The spec goes on to say:
If an action x synchronizes-with a following action y, then we also have hb(x, y).
where hb stands for happens-before, and
An unlock action on monitor m synchronizes-with all subsequent lock actions on m (where "subsequent" is defined according to the synchronization order).
Also note that:
If hb(x, y) and hb(y, z), then hb(x, z).
So in your example, the synchronized(lock) around b will establish a happens-before relationship for the following read, and thus the value of b is guaranteed to be visible in other threads that also use synchronized(lock). Explicitly,
hb(write to b in threadOne, unlock in threadOne) AND
hb(unlock in threadOne, lock in threadTwo) AND
hb(lock in threadTwo, read from a in threadTwo) IMPLIES
hb(write to b in threadOne, read from b in threadTwo)
Similarly, a will be guaranteed to be visible to the other thread. Explicitly,
hb(write to a in threadOne, lock in threadOne) AND
hb(lock in threadOne, unlock in threadOne) AND
hb(unlock in threadOne, lock in threadTwo) AND
hb(lock in threadTwo, read a in threadTwo) IMPLIES
hb(write to a in threadOne, read a in threadTwo).
The write and then subsequent read of c does not have a happens-before relationship, so therefore, according to the spec, the write to c is not necessarily visible to threadTwo.
What if we remove variable c out of the equation? I'm wondering if only b is guaranteed to be visible in threadTwo because it was inside the synchronization block.
Yes, see above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With