简体   繁体   中英

Do while loops in Java not check their conditions if there is no body?

In this example I have a simple JFrame containing a JButton with an ActionListener tied to it. This AcitonListener just changes a boolean flag that should allow the program to complete.

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");
    }
}

This never prints "Finished", and after the button has been clicked once prints

Waiting
Button was clicked!

However, if I modify the while loop to read

while(!flag[0]){
    System.out.println("I should do nothing. I am just a print statement.");
}

This works! The printout looks like

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

I understand this probably isn't the proper way to wait on an action, but nonetheless I am interested in knowing why Java behaves this way.

The likeliest reason is that flag[0] = true; is executed on the UI thread, whereas while(!flag[0]) is executed on the main thread.

Without synchronization, there is no guarantee that the change made in the UI thread will be visible from the main thread.

By adding the System.out.println you introduce a synchronization point (because the println method is synchronized ) and the problem gets solved.

You could make flag a volatile instance or class boolean variable (not an array), or, more simply, put whatever code you want executed on the button being pressed in the listener itself.


For reference, the code with a volatile variable would look like this:

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");
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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