2 回答
好的,仔细看看你的代码,你所拥有的还不够。对共享字段的访问在您的synchronized
块之外,所以不,它不起作用。
此外,Java 要求共享内存的读取和写入都以某种方式“同步”。使用synchronized
keyworld,这通常意味着您需要在读取和写入时都使用它,并且您没有显示写入。
除此之外,用于给定字段集或共享内存的“锁”必须是读取和写入的相同锁。说真的,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级别观察的,如果没有happens-before关系,代码可以从以下优化:
while (flag){
synchronized (lock){
// some work
}
}
到 :
if(flag){
while (true){
synchronized (lock){
//some work
}
}
}
为了确保线程可见性,我们需要避免这种优化,例如通过 volatile 关键字或其他同步策略。循环中sync块的出现类似于增强的volatile关键字的作用,保证了前面变量的可见性,所以当我们第二次循环进入sync块时,可以看到最新的. 变化,这就是为什么循环可以正常停止。看起来不错,但这不是正确的同步方法,所以不要这样做。
有关详细说明,请在此处查看类似问题