简体   繁体   English

Java 线程可见性和同步

[英]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:我一直在用一个流行的例子来试验 Java 线程可见性问题,这个例子是通过共享的布尔值和非易失性变量向线程发送停止信号,而目标线程似乎没有得到它)​​,如下所示:

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.主线程通过将stopped设置为true,5秒后向目标线程发送停止信号,目标线程获取不到,因此不会停止。

Defining stopped variable as volatile obviously solves the problem.stopped变量定义为volatile显然可以解决问题。

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. Bu 然后我意识到,如果我将stopped的变量non volatile ,而是在目标线程的synchronized上下文中访问它,目标线程将获得最终值并停止。 So thread visibility problem seems to be solved just like using volatile .所以线程可见性问题似乎就像使用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?或者我们可以说访问具有互斥性的共享变量似乎会强制目标线程刷新其缓存内存,就像访问volatile变量一样?

If the latter is true which way do you suggest to overcome thread visibility issue, by means volatile keyword or access with mutual exclusion?如果后者是真的,您建议通过 volatile 关键字或互斥访问来克服线程可见性问题的哪种方式?

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.您错过了 Java 语言参考 (JLS) 中讨论 Java 内存模型的章节。 Either that, or you missed working through the concurrency chapter in the Java tutorial.要么,要么你错过了 Java 教程中的并发章节。 https://docs.oracle.com/javase/tutorial/essential/concurrency/ 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.无论哪种方式,您都会了解到,如果线程 A 从同步块中退出,然后线程 B 进入在同一对象上同步的块,则线程 A 在释放锁之前写入的所有内容都保证对线程 B 可见在线程 B 锁定锁之后。

我认为互斥还提供了内存可见性,如 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.但是,最好使用 volatile 共享变量(除非非可见性相关问题需要同步)以获得更高的并发性。 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.同步块或方法的工作方式与关于可见性的 volatile 变量非常相似。 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.在线程退出同步块后,它释放监视器,这具有将 CPU 寄存器刷新到 CPU 缓存的效果 - 以便该线程所做的写入对其他线程可见。

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.在进入同步块之前,线程获取监视器,其作用是使本地处理器缓存(CPU 寄存器)无效,并强制线程从 CPU 缓存中重新读取。 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.在这种情况下,根据 JMM,无法保证何时刷新 CPU 寄存器以及 - 因此,最新值是否可用于其他线程。

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.出于这个原因,您可以假设代码“工作”可能是因为刷新及时发生并且同步块强制线程在每个循环中重新从 CPU 缓存中读取。 Besides, the JMM states that the “ happen-before ” visibility guarantee requires the same monitor lock.此外,JMM 声明“ happens-before ”可见性保证需要相同的监视器锁。 So, anything beyond that is just by chance.因此,除此之外的任何事情都是偶然的。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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