Est-ce que les boucles while en Java ne vérifient pas leurs conditions s'il n'y a pas de corps?


Dans cet exemple, j'ai un JFrame simple contenant un JButton avec un ActionListener lié à celui-ci. Cet AcitonListener change simplement un indicateur booléen qui devrait permettre au programme de se terminer.

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

Cela n'imprime jamais "Fini", et après que le bouton a été cliqué une fois imprime

Waiting
Button was clicked!

Cependant, si je modifie la boucle while pour lire

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

Cela fonctionne! L'impression ressemble à

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

Je comprends que ce n'est probablement pas la bonne façon d'attendre une action, mais néanmoins, je suis intéressé à savoir pourquoi Java se comporte de cette façon.

Author: Aroto, 2015-11-30

1 answers

La raison la plus probable est que flag[0] = true; est exécuté sur le thread de l'INTERFACE utilisateur, tandis que while(!flag[0]) est exécuté sur le thread principal.

Sans synchronisation, il n'y a aucune garantie que la modification apportée dans le thread de l'interface utilisateur sera visible depuis le thread principal.

En ajoutant le System.out.println, vous introduisez un point de synchronisation (car la méthode println est synchronized) et le problème est résolu.

Vous pouvez faire de flag une instance volatile ou une variable booléenne de classe (pas un tableau), ou, plus simplement, mettez le code que vous souhaitez exécuter sur le bouton enfoncé dans l'écouteur lui-même.


Pour référence, le code avec une variable volatile ressemblerait à ceci:

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");
}
 7
Author: assylias, 2015-11-30 17:50:17