Does JMM guarantee the visibility of a synchronized write to the variable that is read in the other thread after a synchronized block? Here's what I mean:
public class SynchronizedWriteRead {
    private int a;
    private int b;
    public void writer() {
        synchronized (this) {
            a = 5;
            b = 7;
        }
    }
    public void reader() {
        synchronized (this) {
            int r1 = a; /* 5 */
        }
        int r2 = b; /* ? */
    }
}
JMM guarantees that an unlock on a monitor happens-before every subsequent lock on that monitor. But I'm not sure if it relates only to the synchronized block body or not.
Recently I'm encountered this post from Aleksey Shipilëv - Safe Publication and Safe Initialization in Java. It says:
Notice how doing
synchronizedin Unsafe DCL store does not help, contrary to layman belief it somehow magically "flushes the caches" or whatnot. Without a paired lock when reading the protected state, you are not guaranteed to see the writes preceding the lock-protected write.
So this is why I asked myself this question. I couldn't find an answer in the JLS.
Let's put it another way. Sometimes you're piggybacking on a volatile happens-before guarantee like this:
public class VolatileHappensBefore {
    private int a; /* specifically non-volatile */
    private volatile int b;
    public void writer() {
        a = 5;
        b = 7;
    }
    public void reader() {
        int r1 = b; /* 7 */
        int r2 = a; /* 5 */
    }
}
You're guaranteed to see both writes because sequential actions in the same thread are backed by happens-before, and happens-before itself is transitive.
Can I use a synchronized happens-before guarantee the same way? Maybe even like this (I've put sync variable to forbid the compiler/JVM to remove otherwise empty synchronized block):
    public void writer() {
        a = 5;
        b = 7;
        synchronized (this) {
            sync = 1;
        }
    }
    public void reader() {
        synchronized (this) {
            int r = sync;
        }
        int r1 = a; /* ? */
        int r2 = b; /* ? */
    }
 
     
     
     
    