Java-Anwendung arbeitet Programm in seltsamer Reihenfolge ab

theuserbl

Bekanntes Mitglied
Unter
Progress Bar - Allgemeine Themen, Fragen und Diskussionen - Jabaco - Community
schrieb jemand ein Problem mit Jabaco (ein BASIC-Dialekt für die JavaVM), das auf Java beruht und mich selbst sehr verwundert.

Hier ein Besipielprogramm in Java:
Java:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class Test extends JFrame implements ActionListener {

  JLabel label;
  JButton button;

  public Test() {
    super ("seltsames Java-Verhalten");
    JPanel p = new JPanel();
    p.setLayout(null);
    label = new JLabel("Hier steht etwas");
    button = new JButton("Klick mich");
    button.addActionListener(this);
    label.setBounds(30,30,350,50);
    button.setBounds(30,150,420,150);
    p.add(label);
    p.add(button);
    add(p);
  }

  public void actionPerformed(ActionEvent e) {
    Object source = e.getSource();

    if (source == button) {
      label.setText("Bitte warten");
      for (int i=0; i<=100000; i++) {
        System.out.println(i);
      }
      label.setText("Ok");
    }
  }

  public static void main(String[] args) {
    Test f = new Test();
    f.setSize(500,400);
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setVisible(true);
  }

}


Entscheidend hierbei ist der Teil, wenn der Button gedrückt wurde:
Java:
label.setText("Bitte warten");
for (int i=0; i<=100000; i++) {
  System.out.println(i);
}
label.setText("Ok");

Seine und meine Erwartungshaltung ist, daß erst das Label den Text "Bitte warten" bekommt, dann die Schleife abgearbeitet wird und zuletzt "Ok" ausgegeben wird.

Wenn man das Programm jedoch startet, bleibt das Label zuerst bei seinem alten Text "Hier steht etwas" und arbeitet die Schleife ab. Und am Ende wird ein "Ok" ausgegeben. "Bitte warten" sieht man erst garnicht. Möglicherweise wird es ganz schnel vor den "Ok" auf "Bitte warten" gesetzt, so schnell daß man es nicht sieht.

Die Frage ist: Warum wird die Schleife vorher abgearbeitet, bevor das Label seinen Text auf "Bitte warten" geändert hat? Und wie muß man dieses Programm abändern, damit es macht, was es eigentlich machen sollte?

Ein "label.updateUI();" nach "label.setText("Bitte warten");" hilft übrigens auch nicht weiter.


Grüße
theuserbl
 
Code:
actionPerformed
wird aus dem Event Dispatch Thread (EDT) aufgerufen, der auch für das Zeichnen der UI verantwortlich ist. Deshalb kann das JLabel logischerweise nichts neues anzeigen, solang diese Methode noch läuft.
Andererseits sollten Swing Komponenten aber nur aus dem EDT verändert werden, da Swing nicht thread safe ist.

Wenn man zusätzlich noch bedenkt, dass "busy waiting" mit einer Schleife ganz schlecht ist (100% CPU-Verbrauch!) könnte der Code so aussehen:
Java:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class Test extends JFrame implements ActionListener {

  private final JLabel label;
  private final JButton button;

  public Test() {
    super ("Kein seltsames Java-Verhalten mehr");
    
    JPanel p = new JPanel();
    p.setLayout(null);
    add(p);
    
    label = new JLabel("Hier steht etwas");
    label.setBounds(30,30,350,50);
    p.add(label);
    
    button = new JButton("Klick mich");
    button.setBounds(30,150,420,150);
    button.addActionListener(this);
    p.add(button);
  }

  public void actionPerformed(final ActionEvent e) {
    Thread thread = new Thread() {
      public void run() {
        Object source = e.getSource();
        if (source == button) {
          beginCalculation();
        }
      }
    };
    thread.start();
  }
  
  public boolean beginCalculation() {
    changeText("Bitte warten");
    try {
      Thread.sleep(1500);
      changeText("Ok");
      return true;
    } catch(InterruptedException e) {
      changeText("Abgebrochen");
      return false;
    }
  }
  
  private void changeText(final String newText) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        label.setText(newText);
      }
    });
  }

  public static void main(String[] args) {
    Test f = new Test();
    f.setSize(500,400);
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setVisible(true);
  }

}
 
Um Dinge NICHT im Event Dispatch Thread (EDT) laufen zu lassen (die for-Schleife), aber trotzdem vorher, während, oder danach die GUI ändern zu können, empfiehlt sich ein Blick in javax.swing.SwingWorker
 

Neue Themen


Zurück
Oben