I am looking at one of the simplest examples I found and started to reason about SO (synchronization order) or more precise, lack of it. Consider the example below:
int a, b; // two shared variables
Thread-X:
void threadX() {
synchronized(this) {
a = 1;
}
synchronized(this) {
b = 1;
}
}
And a reader thread, Thread-Y:
void threadY() {
int r1 = b;
int r2 = a;
}
For simplicity let's suppose that Thread-Y does the reads exactly in this order: it will for sure first read b and then a (in reverse of the writing that is).
Reading thread is allowed to see [1, 0] (like b=1 happened before a=1). I think I understand also why: because there is no synchronization order between the two actions, and as such there is no happens-before and according to the JLS this is a data race:
When a program contains two conflicting accesses that are not ordered by a happens-before relationship, it is said to contain a data race.
Thus reading a and b are two racy reads, so seeing b=1 and a=0 is allowed and possible.
Now this in turn allows JVM to do the lock coarsening in the writer, so it becomes:
void threadX() {
synchronized(this) {
a = 1;
b = 1;
}
}
My question is, if the reader was initially written like this:
void threadY() {
synchronized(this) {
int r1 = b;
}
synchronized(this) {
int r2 = a;
}
}
Would lock coarsening still be allowed? I think I know the answer, but I would want to hear an educated explanation too.