I’ve read from this article that:
…Synchronized blocks also guarantee that all variables accessed inside the synchronized block will be read in from main memory, and when the thread exits the synchronized block, all updated variables will be flushed back to main memory again, regardless of whether the variable is declared
volatile
or not.
There’s also an example showed in Effective Java:
public class StopThread { private static boolean stopRequested; private static synchronized void requestStop() { stopRequested = true; } private static synchronized boolean stopRequested() { return stopRequested; } public static void main(String[] args) throws InterruptedException { Thread backgroundThread = new Thread(() -> { int i = 0; while (!stopRequested()) i++ }); backgroundThread.start(); TimeUnit.SECONDS.sleep(1); requestStop(); } }
The variable stopRequested
is not declared as volatile
and the author states that “…In other words, the synchronization on these methods is used solely for its communication effects, not for mutual exclusion…”. But I would like to know that, in case of accessing an element of an array or accessing a field of an object in a synchronized
method/block, could we always guarantee the memory visibility, without having to manually force a volatile access of the array’s element (by using Unsafe#getObjectVolatile
for example) or declare the volatile
modifier on the object’s field? Thanks!
// flags is an instance of a custom class. Should Flags.stopRequested be declared volatile? public class StopThread { private static Flags flags = new Flags(); private static synchronized void requestStop() { flags.stopRequested = true; } private static synchronized boolean stopRequested() { return flags.stopRequested; } }
// flags is an array. Should we use getObjectVolatile/putObjectVolatile to access the element? public class StopThread { private static boolean[] flags = new boolean[n]; private static synchronized void requestStop() { flags[m] = true; } private static synchronized boolean stopRequested() { return flags[m]; } }
Advertisement
Answer
In the first example, flags is initialised using a static
initialiser. It is guaranteed by the Java Memory Model that any subsequent reads would see the updated value of the reference, and the correct initial state of Flags
(basically, Flags
would be properly published).
However, since Flags
is mutable and might be mutated at a later point in time by multiple threads, you need to use proper synchronisation to ensure memory visibility for its state. So a volatile
would be needed for its fields (or proper synchronisation).
In the second example, simply declaring flags
as volatile
won’t ensure memory visibility of writes to arrays. It just ensures a happens-before relationship b/w writes to the array reference and subsequent reads from it. To ensure a happens-before relationship b/w writes to array elements and subsequent reads from them, you need to use locking, which you are already doing.
Why this works? The JMM guarantees a happens-before relationship b/w the release of a monitor and its re-acquisition. When a thread releases a lock which is later acquired by another thread, a kind-of total ordering (governed by happens-before) is ensured b/w whatever writes happened in the previous thread and any subsequent reads by the thread that re-acquired the lock.
Just remember that declaring a reference as volatile
does not ensure proper visibility of the mutable state of the object it refers to. We still need proper synchronisation mechanisms to ensure memory visibility.