简体   繁体   中英

Java Thread Visibility and Synchronized

I have been experimenting Java thread visibility problem with the popular example of sending a stop signal to the thread by means of a shared boolean and non-volatile variable and the target thread does not seem to get it) as below:

public class ThreadVisibilityTest {

    //Shared variable to send a signal to the thread
    static boolean stopped = false;

    public static void main(String[] args) throws Exception {

        Thread targetThread = new Thread(new Runnable() {
                public void run() {
                while(!stopped) {}
                System.out.println("Target thread gets signal and stops...");
                }
                });

        targetThread.start();

        TimeUnit.SECONDS.sleep(5);
        stopped=true;
        System.out.println("Main thread has sent stop signal to the thread...");

    }

}

Main thread sends stop signal to the target thread after 5 seconds by means of setting stopped to true and the target thread can not get it and so does not stop.

Defining stopped variable as volatile obviously solves the problem.

Bu then I realized that if I make stopped variable non volatile but instead access it in a synchronized context in the target thread, target thread gets the final value and stops. So thread visibility problem seems to be solved just like using volatile .

Thread targetThread = new Thread(new Runnable() {
            public void run() {
                while(true) {
                    synchronized(this) {
                        if(stopped) break; 
                    }
                }
                System.out.println("Target thread gets signal and stops...");
            }
        });

And also the object monitor to be used for synchronization seems to have no effect as follows:

synchronized(Thread.class) {
        if(stopped) break; 
}

Is this something that happens by chance or do I miss something? Or can we say that accessing shared variable with mutual exclusion seems to force target thread to refresh its cache memory just like accessing a volatile variable?

If the latter is true which way do you suggest to overcome thread visibility issue, by means volatile keyword or access with mutual exclusion?

Thanks in advance

Is this something that happens by chance or do I miss something?

You missed the chapter in the Java Language Reference (JLS) that talks about the Java Memory Model. Either that, or you missed working through the concurrency chapter in the Java tutorial. https://docs.oracle.com/javase/tutorial/essential/concurrency/

Either way, you would have learned that if thread A exits from a synchronized block, and then thread B enters a block that is synchronized on the same object , then everything that thread A wrote before releasing the lock is guaranteed to be visible to thread B after thread B locks the lock.

我认为互斥还提供了内存可见性,如 Java Concurrency In Practice (By Brian Goetz) 在第 3.1.3 节锁定和可见性中所述。

See, you are reading it in synchronized context but not writing in the synchronized context. That might cause problem.

As other answers have already pointed out, memory visibility is established when using synchronization.

However, it would be preferable to use a volatile shared variable (unless synchronization is needed for non visibility related issues) for greater concurrency. Overuse of synchronization forces threads to constantly wait for each other, when it would be safe and faster to work concurrently.

Synchronized blocks or methods work very similarly to volatile variables regarding visibility. After the thread exits the synchronized block, it releases the monitor, which has the effect of flushing CPU registers to CPU cache - so that writes made by that thread are visible to other threads.

Before entering the synchronized block, the thread acquires the monitor, which has the effect of invalidating the local processor cache (CPU registers) and forcing the thread re-read from the CPU cache. Therefore, all changes made by the previous release would be visible to the thread which has the same monitor lock.

In the above example, the write was made outside of synchronized context. In this scenario, according to JMM, there is no guarantee as to when the flush CPU register will occur and - consequently, whether the latest values would be available to other threads.

For this reason, you can assume that the code “works” probably because the flush occurs in time and the synchronized block forces the thread to re-read from CPU cache on each loop. Besides, the JMM states that the “ happen-before ” visibility guarantee requires the same monitor lock. So, anything beyond that is just by chance.

Source: https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#synchronization

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM