简体   繁体   English

如果在同步锁周围的循环中使用变量,是否将其从“主存储器中刷新”读取?

[英]Will a variable be read “fresh from main memory” if used in a loop AROUND a synchronized lock?

Please see the code below: 请看下面的代码:

private  static boolean flag=true; // main thread will call flag=false

private final static Object lock=new Object(); // lock condition

public static void thread1(){

    while (flag){

        synchronized (lock){
            // some work
        }

    }

}


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

    Thread t1=new Thread(()->{
        thread1();
    });
    t1.start();
    Thread.sleep(1000);
    flag=false;

    // The program can stop normally

}

No matter at any time , When one thread entering the synchronized block, will the value of the variable flag be loaded from main memory? 无论何时,当一个线程进入同步块时,是否将从主存储器中加载变量标志的值?

Thank you for your detailed explanation, because I am not sure if the flag has a happend-befores relationship. 感谢您的详细说明,因为我不确定该标志是否具有事前关系。 Literally, the flag is not in the synchronized block. 从字面上看,该标志不在同步块中。

Update1: UPDATE1:

I know that using volatile can and I also know how to write the right code,, but I want to know now if there is no volatile keyword. 我知道使用volatile可以,而且我也知道如何编写正确的代码,但是我现在想知道是否没有volatile关键字。 Whether synchronized can guarantee visibility. 是否同步可以保证可见性。 Note: the flag variable is not in synchronized block. 注意:标志变量不在同步块中。

Update2: UPDATE2:

I updated the code again, the code on my win10+JDK8 system can stop normally, do you think it is correct or accidental, because it is not tested on all hardware systems, so I need theory to guide。 我再次更新了代码,我的win10 + JDK8系统上的代码可以正常停止,您认为它是正确的还是偶然的,因为尚未在所有硬件系统上进行测试,因此我需要理论指导。

Focus on the question: 关注以下问题:

whether the loop condition (flag variable) has a happen-before relationship with the synchroized block inside the loop,If it has a happen-before relationship, jvm whether ensure that the flag variable is loaded from main memory even if the flag variable is not in the synchronized block. 循环条件(标志变量)是否与循环内的同步块具有先发生关系,如果存在先发生关系,jvm是否确保从主存储器加载了标志变量,即使标志变量没有在同步块中。

If every one think there is no happen-before relationship, then how do you explain that when I remove the synchronized block, the code will loop indefinitely. 如果每个人都认为不存在事前关系,那么您如何解释当我删除同步块时,代码将无限期循环。 When I add it, it will stop normally. 当我添加它时,它将正常停止。 Is this just an accident? 这只是意外吗?

OK looking a little more closely at your code, what you have is not enough. 可以再仔细看看您的代码,您所拥有的还不够。 The access to a shared field is outside of your synchronized block, so no it does not work. 对共享字段的访问不在您的synchronized块之外,因此不行。

In addition, Java requires that both the read and the write of shared memory be "synchronized" somehow. 另外,Java要求共享内存的读取和写入都必须以某种方式“同步”。 Using the synchronized keyworld, that usually means you need to use it on both the read and the write, and you did not show the write. 使用synchronized密钥世界,通常意味着您需要在读取和写入过程中都使用它,而没有显示写入。

And in addition to that, the "lock" that you use for a given set of fields or shared memory must be the same lock for both the read and the write. 除此之外,用于给定的一组字段或共享内存的“锁”对于读取和写入必须是相同的锁。 Seriously, volatile is a lot easier here, and the API in java.util.concurrent is even easier and recommended. 认真地说,这里的volatile容易得多,并且java.util.concurrent的API甚至更容易并且建议使用。 Don't try reinventing the wheel. 不要尝试重新发明轮子。

private static boolean flag = true; // must use 'resetFlag'

public void resetFlag() { synchronized( "lock" ) {flag = false;} }

public boolean getFlag() { synchronized( "lock" ) {return flag;} }

public void thread1() {
    while ( getFlag() ){
        synchronized ("lock"){
            // other work
        }
    }
}

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

    Thread t1=new Thread(()->{
        thread1();
    });
    t1.start();
    Thread.sleep(1000);
    resetFlag();

    // The program can stop normally

}

I think the above has the required changes. 我认为以上已作了必要的更改。

Regarding your second update: the code on my win10+JDK8 system can stop normally Yes it can. 关于第二次更新: the code on my win10+JDK8 system can stop normally ,可以。 Memory visibility is not guaranteed, but it is not prohibited. 不能保证内存可见性,但不是禁止的。 Memory can be made visible for any reason, even just "accidentally." 可以出于任何原因使内存可见,即使只是“偶然”。 On Intel platforms, Intel has a QPI bus which exchanges memory update information at high speed, bypassing the memory bus. 在Intel平台上,Intel具有QPI总线,可绕过内存总线高速交换内存更新信息。 However even that can be got around by software, so it's best to just put the synchronization where needed (Hint: look at AtomicBoolean .) 但是即使可以通过软件解决,所以最好将同步放在需要的地方(提示:看一下AtomicBoolean 。)

Thanks to the information provided by @xTrollxDudex and @markspace ,The code in the loop section is observed from the jvm level, If there is no happens-before relationship and the code may be optimized from : 由于@xTrollxDudex和@markspace提供的信息,循环部分中的代码是从jvm级别观察的,如果没有之前发生的关系,则可以从以下代码进行优化:

       while (flag){

        synchronized (lock){
            // some work
        }

    }

to : 至 :

      if(flag){

        while (true){

            synchronized (lock){
                //some work
            }

        }

    }

To ensure thread visibility, we need to avoid this optimization, such as through the volatile keyword or other synchronization strategies. 为了确保线程可见性,我们需要避免这种优化,例如通过volatile关键字或其他同步策略。 The appearance of the sync block in the loop is similar to the function of the enhanced volatile keyword, which guarantees the visibility of the variable in front of it, so when we loop into the sync block for the second time, we can see it latest. 循环中同步块的外观类似于增强型volatile关键字的功能,该功能保证了变量前面的可见性,因此当我们第二次循环进入同步块时,可以看到最新的。 The change, which is why the loop can stop normally. 更改,这就是循环可以正常停止的原因。 It looks fine, but it's not the right synchronization method, so don't do it. 看起来不错,但这不是正确的同步方法,所以不要这样做。

For a detailed explanation, please see a similar question in here 有关详细说明,请参见此处的类似问题

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

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