Java无法跳出while循环

标签 java

我正在尝试用java构建一个计时器。

预期输出:

当你运行程序时,会出现一个窗口,里面有一个大“0”。

一旦用户按下键盘上的任意键,数字就会每秒增加 1。

这就是我所拥有的。

    import javax.swing.*;
    import javax.swing.SwingConstants;
    import java.awt.BorderLayout;
    import java.awt.Font;
    import java.awt.event.KeyListener;
    import java.awt.event.KeyEvent;

    public class TimerTest implements KeyListener {
        static final int SCREEN_WIDTH = 960;
        static final int SCREEN_HEIGHT = 540;
        static boolean timerStarted;
        static int keyPressedNum; //amount of times the user pressed any key.

        static JLabel label;
        static int currentTime;

    public static void main(String[] args) {

        JFrame frame = new JFrame("TimerTest");
        JPanel panel = new JPanel();

        label = new JLabel();
        label.setFont(new Font("Arial", Font.PLAIN, 256));
        label.setText("0");
        label.setHorizontalAlignment(SwingConstants.CENTER);

        panel.setLayout(new BorderLayout());
        panel.add(label, BorderLayout.CENTER);

        frame.addKeyListener(new TimerTest());

        frame.getContentPane().add(panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
        frame.setResizable(false);
        frame.setVisible(true);

        while (true) {
            while (TimerTest.timerStarted == false) {
                if (TimerTest.timerStarted == true) {break;}
            }

            try {Thread.sleep(1000);}
            catch (InterruptedException ex) {ex.printStackTrace();}

            currentTime++;
            label.setText(String.valueOf(currentTime));
        }
    }

    public void keyPressed(KeyEvent e) {
        System.out.println("Keypress indicated.");
        TimerTest.timerStarted = true;
    }

    public void keyReleased(KeyEvent e) {}
    public void keyTyped(KeyEvent e) {}
}

当我按任意键时,程序将 timerStarted 设置为 true。

因为 timerStarted 为 true,所以我期待这部分:

    while (TimerTest.timerStarted == false) {
            if (TimerTest.timerStarted == true) {break;}
    }

跳出循环。

相反,当我运行程序并按任意键时,命令行只会打印:Keypress indicates.,并且它不会跳出该循环。

现在这是奇怪的部分:

我尝试向 while block 添加一些代码,如下所示。

  while (TimerTest.timerStarted == false) {
        System.out.println("currently stuck here...");
        //(I simply added a println code)
        if (TimerTest.timerStarted == true) {break;}
  }

令人惊讶的是,当我这样做时,程序就像我想要的那样工作。它会跳出该循环并且计时器开始运行。

这让我很困惑。添加 System.out.println(); 解决了问题...?

为什么没有 System.out.println() 让我无法跳出循环?

任何解释将不胜感激 =)

最佳答案

问题是您的代码不是线程安全的。具体来说,您没有执行确保 timerStarted 的读取看到其他线程的最新写入所需的操作。

有许多可能的解决方案,包括:

  • 声明timerStarted volatile ,或者
  • 同步 block 中读取和写入timerStarted,或者
  • 使用AtomicBoolean

这些确保一个线程写入标志与第二个线程随后读取标志之间存在一种发生之前关系。这反过来保证了读取看到写入的值。

"Why does not having System.out.println() makes me unable to break out of the loop?"

因为不存在发生在之前的关系;见上文。

一个更有趣的问题是:

"Why does it work when you add the println?"

System.out.println() 在后台执行一些同步操作。 (流对象是线程安全的,并进行同步以确保其数据结构不会困惑。)这显然足以导致对 timerStarted 的写入刷新到主内存。

但是,这并不能保证。 javadoc 没有指定 println 调用的同步行为。此外,尚不清楚这是否足以在 timerStarted 的写入和读取之间发生(偶然!)。

引用文献:

关于Java无法跳出while循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59509342/

相关文章:

java - 使用 spring 更新单个对象并通过 url 发送

java - 为什么不能以编程方式滚动 ScrollView?

java - Android 获取相对于地平线的角度

java - Kafka Streams Global Store - 添加更改日志主题

Java-确定选择了哪个单选按钮

在 ubuntu 中下载 gradle 插件时出现 Java 安全问题

java - 将变量分配给 null 的最佳替代方法是什么?

java - 当我构建 Spring Boot 的 Web 项目时,我无法使用 freemarker

java - 数组输入数字0-2,输出对应字母a,b,c

java - 如何将证书链及其证书导入 Java keystore ?