2
4

2 回答 2

3

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

于 2018-07-09T03:39:10.143 回答
1

感谢@xTrollxDudex 和@markspace 提供的信息,循环部分的代码是从jvm级别观察的,如果没有happens-before关系,代码可以从以下优化:

       while (flag){

        synchronized (lock){
            // some work
        }

    }

到 :

      if(flag){

        while (true){

            synchronized (lock){
                //some work
            }

        }

    }

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

有关详细说明,请在此处查看类似问题

于 2018-07-09T07:47:28.480 回答