在这个例子中,我有一个简单的JFrame包含一个绑定了ActionListener的JButton.这个AcitonListener只是更改了一个允许程序完成的布尔标志.
public class Test { public static void main(String[] args){ final boolean[] flag = new boolean[1]; flag[0] = false; JFrame myFrame = new JFrame("Test"); JButton myButton = new JButton("Click Me!"); myButton.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent arg0) { System.out.println("Button was clicked!"); flag[0] = true; } }); myFrame.add(myButton); myFrame.setSize(128,128); myFrame.setVisible(true); System.out.println("Waiting"); while(!flag[0]){} System.out.println("Finished"); } }
这永远不会打印“完成”,并且在点击按钮后打印一次
Waiting Button was clicked!
但是,如果我修改while循环来读取
while(!flag[0]){ System.out.println("I should do nothing. I am just a print statement."); }
这有效!打印输出看起来像
Waiting I should do nothing. I am just a print statement. I should do nothing. I am just a print statement. .... I should do nothing. I am just a print statement. Button was clicked! Finished
我理解这可能不是等待某个动作的正确方法,但我仍然有兴趣知道为什么Java会以这种方式运行.
解决方法
最有可能的原因是flag [0] = true;在UI线程上执行,而while(!flag [0])在主线程上执行.
如果没有同步,则无法保证UI线程中所做的更改将从主线程中可见.
通过添加System.out.println,您将引入同步点(因为println方法已同步)并且问题得到解决.
您可以将标志设置为易失性实例或类布尔变量(不是数组),或者更简单地,将要执行的任何代码放在侦听器本身中按下的按钮上.
作为参考,带有volatile变量的代码如下所示:
private static volatile boolean flag; public static void main(String[] args) { JFrame myFrame = new JFrame("Test"); JButton myButton = new JButton("Click Me!"); myButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { System.out.println("Button was clicked!"); flag = true; } }); myFrame.add(myButton); myFrame.setSize(128,128); myFrame.setVisible(true); System.out.println("Waiting"); while (!flag) { } System.out.println("Finished"); }