Java 循环未终止时计时
运行此代码时,我希望它将测试变量递增5秒,然后完成Java 循环未终止时计时,java,timer,while-loop,timertask,Java,Timer,While Loop,Timertask,运行此代码时,我希望它将测试变量递增5秒,然后完成 import java.util.Timer; import java.util.TimerTask; public class Test { private static boolean running; public static void main( String[] args ) { long time = 5 * 1000; // converts time to millisecond
import java.util.Timer;
import java.util.TimerTask;
public class Test {
private static boolean running;
public static void main( String[] args ) {
long time = 5 * 1000; // converts time to milliseconds
long test = Long.MIN_VALUE;
running = true;
// Uses an anonymous class to set the running variable to false
Timer timer = new Timer();
timer.schedule( new TimerTask() {
@Override
public void run() { running = false; }
}, time );
while( running ) {
test++;
}
timer.cancel();
System.out.println( test );
}
}
然而,当我运行它时,程序并没有结束(我假设,我已经给了它一段合理的时间)。但是,如果我将while循环更改为
while( running ) {
System.out.println();
test++;
}
程序在预期的时间内完成(并打印出许多行)。我不明白。为什么会发生这种行为?根据我们的说法,无法保证非易失性字段何时会从另一个线程中可见。在您的例子中,您的主要方法是JIT编译的,JIT编译器合理地假设,由于当前线程没有修改循环中运行的变量,因此我们不应该在每次迭代时读取它,并将循环转换为无限循环。关于这件事,甚至没有任何证据
添加System.out.println
调用时,JIT编译器似乎无法将其内联,因此无法确保此方法不会修改运行的变量,从而关闭字段读取优化。然而,这不应被视为问题的解决方案:即使您的内部有System.out.println
,Java的新版本可能会更智能并优化您的循环。让我们更深入地了解您的代码
如果你调试你的代码,它会工作。这是因为您将只看到一个线程,而没有缓存。Java可以使用基于线程的缓存机制。您需要将其读写到主内存中
因此,如果您在运行的变量上使用volatile
关键字,jvm会将其视为可由多个线程编辑,并且不应该缓存它
因此,在您的情况下,解决方案是将volatile
添加到您的运行变量。您正在更新运行
变量,该变量位于与main
方法不同的线程中。Java内存模型不保证在不同线程中看到非易失性变量的更新
最简单的解决方案是将变量设置为volatile
:这会在线程访问变量时强制检查变量的“当前”值
其他解决方案包括使用AtomicBoolean
而不是boolean
,以及将对运行
的访问包装在相互同步的
块中(即,访问运行
的代码与更新它的代码在同一监视器上同步)
我强烈建议您在实践中选择Java并发的一个副本,它详细描述了这个问题。如果让运行变得不稳定会怎么样?
?我刚刚使用JDK 7和IntelliJ 11测试了您的代码,两个版本都在几秒钟内完成。你用什么来跑?@AndyTurner哇,我以前从未遇到过volatile关键字。这使得代码能够工作。谢谢@TimBiegeleisen Java 8 update 45I接受了这个答案,因为它链接了其他信息。谢谢