问题描述
好的,我已经在 java 8、11 和 14 上测试了这段代码,它们都有相同的结果。 这是不好的做法,也是一个不切实际的场景,但我想了解导致这种情况发生的 JVM 内部原理。
如果你运行这段代码,你会注意到除了 system.out.println 里面的打印部分本身之外的所有东西都执行了。 在某些时候使用稍微不同的 Java 版本,我设法通过将“播放”更改得太不稳定来打印它,但即使这样现在也不起作用。
请至少在声称它只是使变量死锁或使用缓存之前测试代码,事实并非如此,if 执行并且其中的所有内容都可以工作,除了打印部分本身。
public class Main {
public static void main(String[] args) {
TestClass t = new TestClass();
System.out.println("Starting test");
new MyRunnable(t).start();
while (true)
t.testUpdate(System.currentTimeMillis());
}
}
public class MyRunnable extends Thread {
private TestClass t;
public MyRunnable(TestClass t) {
this.t = t;
}
@Override
public void run() {
try {
Thread.sleep(500L);
t.setPlay(true);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class TestClass {
private boolean play = false;
private long lastUpdate = 0;
private long updateRate = 2000;
private boolean hasSysoBeenHit = false;
public void testUpdate(long callTime) {
System.out.println(play);
System.out.println((callTime-lastUpdate));
if (this.play && ((callTime-lastUpdate) >= updateRate)) {
System.out.println("Updating! " + (hasSysoBeenHit = true));
this.lastUpdate = callTime;
}
System.out.println("hasbeenhit? " + hasSysoBeenHit);
}
public void setPlay(boolean t) {
System.out.println("Starting game...");
this.play = t;
}
}
解决方法
您的代码在 TestClass.play 字段上遇到数据竞争:有 2 个线程访问此字段,其中至少有一个执行写入操作。 @aerus 已经表明了这一点。
如果您使字段变得易变,则数据竞争将被消除。在 Java 内存模型中查找 volatile 变量规则。
我还将播放检查的逻辑移到 testUpdate 方法的开头:
public void testUpdate(long callTime) {
if(!play)return;
...