为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

慕哥6287543 2021-05-14 09:05:04
请看下面的代码:private  static boolean flag=true; // main thread will call flag=falseprivate final static Object lock=new Object(); // lock conditionpublic 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是否确保从主存储器中加载了标志变量,即使标志变量没有在同步块中。如果每个人都认为没有事前发生的关系,那么您如何解释当我删除同步块时,代码将无限期地循环。当我添加它时,它将正常停止。这只是意外吗?
查看完整描述

2 回答

?
素胚勾勒不出你

TA贡献1827条经验 获得超9个赞

可以更仔细地查看您的代码,您所拥有的还不够。对共享字段的访问不在您的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。)。


查看完整回答
反对 回复 2021-05-19
?
翻阅古今

TA贡献1780条经验 获得超5个赞

由于@xTrollxDudex和@markspace提供的信息,可以从jvm级别观察循环部分中的代码,如果不存在事前关系,则可以从以下代码进行优化:


       while (flag){


        synchronized (lock){

            // some work

        }


    }

至 :


      if(flag){


        while (true){


            synchronized (lock){

                //some work

            }


        }


    }

为了确保线程可见性,我们需要避免这种优化,例如通过volatile关键字或其他同步策略。循环中同步块的外观类似于增强型volatile关键字的功能,它保证了变量前面的可见性,因此当我们第二次循环进入同步块时,可以看到最新的。所做的更改,这就是循环可以正常停止的原因。看起来不错,但这不是正确的同步方法,所以不要这样做。


查看完整回答
反对 回复 2021-05-19
  • 2 回答
  • 0 关注
  • 157 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信