繁体   English   中英

Java无法跳出while循环

[英]Java unable to break out of while loop

我正在尝试用 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是真的,我期待这部分:

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

跳出循环。

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

现在这是奇怪的部分:

我尝试在 while 块中添加一些代码,就像这样。

  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()使我无法跳出循环?

任何解释将不胜感激=)

您的问题是因为您使用的是非线程安全boolean变量timerStarted

此外, while(true)循环是从主线程运行的,但是您对timerStarted所做的修改是从另一个线程完成的,这导致意外行为完全取决于运行程序的机器。

这种情况会在更改timerStarted值和检查该更改之间产生竞争条件

我是如何检测到这一点的:

只是我在检查值更改和修改值时打印了线程 ID

解决方案

检查这些代码更改

static AtomicBoolean timerStarted = new AtomicBoolean(false);

   while (Scratch.timerStarted.get() == false) {
        System.out.println("Thread.ID: " + Thread.currentThread().getId() + " ----> Scratch.timerStarted: " + Scratch.timerStarted);
        if (Scratch.timerStarted.get() == true) {
            break;
        }
    }

 public void keyPressed(KeyEvent e) {
        System.out.println("Keypress indicated.");
        Scratch.timerStarted.set(true);
        System.out.println("Thread.ID: " + Thread.currentThread().getId() + " ----> Scratch.timerStarted: " + Scratch.timerStarted);
    }

在这里,我使用了AtomicBoolean而不是线程安全的boolean


您需要阅读的内容:

  1. java中的多线程(检查这个: https : //www.javatpoint.com/multithreading-in-java
  2. AtmoicBoolean vs boolean(检查这个: 我什么时候需要在 Java 中使用 AtomicBoolean?

问题是您的代码不是线程安全的。 具体来说,您没有做确保timerStarted读取看到另一个线程的最新写入所需的事情。

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

  • timerStarted声明为volatile ,或
  • synchronized块中读写timerStarted ,或
  • 使用AtomicBoolean

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

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

因为没有发生之前的关系; 看上面。

一个更有趣的问题是:

“为什么添加 println 时它会起作用?”

System.out.println()System.out.println()做了一些同步。 (流对象是线程安全的,并进行同步以确保其数据结构不会被弄乱。)这显然足以导致对timerStarted写入刷新到主内存。

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

参考:

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM