3

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];
    }
}
1
  • 1
    Re, "Synchronized blocks...guarantee that all variables...will be read in from main memory, and...all updated variables will be flushed back to main memory" That certainly is one possible implementation, but the actual guarantee is more general than that. You can sum it up this way: The Java Language Spec guarantees that whatever one thread does to shared variables before it leaves a synchronized block will become visible to any other thread that subsequently enters a block that is synchronized on the same lock object. Commented Jul 2, 2020 at 13:05

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks a lot for the answer! So in my understanding if the flags in the two examples could only be accessed by the two synchronized method (requestStop and stopRequested), there will be no need to declare Flags.stopRequested or boolean[] flags as volatile, nor to use getObjectVolatile or putObjectVolatile to access the array, because synchronized keyword has already guarantee the happens-before relationship between threads. Is that right?
Exactly, no need to use volatile. volatile anyway is a much weaker primitive to proper synchronisation, and you have used proper synchronisation already.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.