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

请看下面的代码:


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


}

无论何时,当一个线程进入同步块时,是否会从主存储器中加载变量标志的值?


感谢您的详细说明,因为我不确定该标志是否具有事前关系。从字面上看,该标志不在同步块中。


更新1:


我知道使用volatile可以,而且我也知道如何编写正确的代码,但是我现在想知道是否没有volatile关键字。是否同步可以保证可见性。注意:标志变量不在同步块中。


更新2:


我再次更新了代码,我的win10 + JDK8系统上的代码可以正常停止,您认为它是正确的还是偶然的,因为尚未在所有硬件系统上进行测试,因此我需要理论指导。


关注以下问题:


循环条件(标志变量)是否与循环内的同步块具有先发生关系,如果存在先发生关系,jvm是否确保从主存储器中加载了标志变量,即使标志变量没有在同步块中。


如果每个人都认为没有事前发生的关系,那么您如何解释当我删除同步块时,代码将无限期地循环。当我添加它时,它将正常停止。这只是意外吗?


慕哥6287543
浏览 158回答 2
2回答

素胚勾勒不出你

可以更仔细地查看您的代码,您所拥有的还不够。对共享字段的访问不在您的synchronized阻止范围内,因此不行。另外,Java要求以某种方式“同步”共享内存的读取和写入。使用synchronizedkeyworld,通常意味着您需要在读取和写入上都使用它,而没有显示写入。除此之外,用于给定的一组字段或共享内存的“锁”对于读取和写入必须是相同的锁。认真地说,volatile这里要容易得多,并且其中的APIjava.util.concurrent甚至更容易推荐。不要尝试重新发明轮子。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}我认为以上已作了必要的更改。关于第二次更新:the code on my win10+JDK8 system can stop normally 是的。不能保证内存可见性,但不是禁止的。可以出于任何原因使内存可见,即使只是“偶然地”。在Intel平台上,Intel具有QPI总线,可绕过内存总线高速交换内存更新信息。但是即使可以通过软件解决,所以最好只是将同步放在需要的地方(提示:请看AtomicBoolean。)。

翻阅古今

由于@xTrollxDudex和@markspace提供的信息,可以从jvm级别观察循环部分中的代码,如果不存在事前关系,则可以从以下代码进行优化:       while (flag){        synchronized (lock){            // some work        }    }至 :      if(flag){        while (true){            synchronized (lock){                //some work            }        }    }为了确保线程可见性,我们需要避免这种优化,例如通过volatile关键字或其他同步策略。循环中同步块的外观类似于增强型volatile关键字的功能,它保证了变量前面的可见性,因此当我们第二次循环进入同步块时,可以看到最新的。所做的更改,这就是循环可以正常停止的原因。看起来不错,但这不是正确的同步方法,所以不要这样做。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java