1. Was ist der EDT (Event Dispatch Thread)
In grafischen Java-Anwendungen — sei es Swing oder JavaFX — werden alle Benutzeraktionen (Klicks, Tasten) sowie das Neuzeichnen von Fenstern in einem speziellen Thread namens EDT (Event Dispatch Thread) verarbeitet.
Wozu wird er benötigt? UI‑Komponenten in Java sind nicht threadsicher. Um Race Conditions und Artefakte zu vermeiden, werden alle Änderungen der Oberfläche strikt an genau einer Stelle ausgeführt — im EDT. Das ist wie eine Kasse mit nur einem Kassierer: Am selben Beleg können nicht gleichzeitig mehrere Personen arbeiten.
In Swing führt der EDT Ereignis-Handler und das Neuzeichnen der Komponenten aus (z. B. actionPerformed). In JavaFX ist das Pendant der JavaFX Application Thread, auf dem UI‑Updates und Handler wie setOnAction ausgeführt werden.
2. Das Problem „langer Operationen“ in der UI
Was passiert, wenn man im EDT eine lang andauernde Operation startet?
Wenn der Benutzer eine Schaltfläche drückt, läuft der Handler (z. B. actionPerformed oder setOnAction) im EDT. Starten Sie darin eine schwere Aufgabe (Lesen einer großen Datei, Netzwerkaufruf, aufwendige Berechnungen), friert die gesamte UI ein:
- Das Fenster reagiert nicht mehr auf Klicks und Tasten.
- Das Neuzeichnen funktioniert nicht mehr — beim Verschieben „friert“ das Fenster ein.
- Der Benutzer glaubt, das Programm sei „abgestürzt“.
Beispiel für falschen Code (Swing):
button.addActionListener(e -> {
// Lange Operation direkt im EDT!
longOperation(); // Zum Beispiel das Lesen einer großen Datei
label.setText("Fertig!");
});
Ergebnis: Solange longOperation() läuft, reagiert das Fenster nicht auf den Benutzer.
Warum ist das so? Der EDT verarbeitet Aufgaben nacheinander und kann immer nur eine gleichzeitig ausführen. Solange er mit Ihrer langen Operation beschäftigt ist, kann er weder Klicks noch Neuzeichnen bearbeiten.
3. Lösung: Lange Operationen nur in Hintergrund-Threads
Prinzip:
- Alle lang andauernden Operationen gehören in Hintergrund-Threads.
- Alle UI‑Änderungen — nur im EDT/JavaFX Application Thread.
Eine lange Operation in einem separaten Thread starten
Beispiel (Swing):
button.addActionListener(e -> {
new Thread(() -> {
longOperation(); // Läuft in einem Hintergrund-Thread
// Jetzt die UI aktualisieren — aber nur aus dem EDT!
SwingUtilities.invokeLater(() -> label.setText("Fertig!"));
}).start();
});
Beispiel (JavaFX):
button.setOnAction(e -> {
new Thread(() -> {
longOperation();
// UI über Platform.runLater aktualisieren
Platform.runLater(() -> label.setText("Fertig!"));
}).start();
});
Wie aktualisiert man die UI aus einem Hintergrund-Thread?
- Swing: verwenden Sie SwingUtilities.invokeLater(Runnable) — die Aufgabe landet in der Warteschlange des EDT.
- JavaFX: verwenden Sie Platform.runLater(Runnable) — die Aufgabe wird im JavaFX Application Thread ausgeführt.
Warum kann man nicht einfach label.setText(...) aus einem Hintergrund-Thread aufrufen? Weil das die Thread-Sicherheit der UI verletzt: Komponenten dürfen nur aus dem UI‑Thread geändert werden.
Spezielle Klassen für Hintergrundaufgaben
In realen Anwendungen muss man häufig Fortschritt anzeigen, Abbruch erlauben und Fehler behandeln. Dafür gibt es:
- SwingWorker<T, V> — für Swing;
- Task<V>, Service<V> — für JavaFX.
Beispiel (JavaFX Task):
Task<Void> task = new Task<>() {
@Override
protected Void call() throws Exception {
longOperation();
// Fortschritt kann aktualisiert werden: updateProgress(...)
return null;
}
};
task.setOnSucceeded(e -> label.setText("Fertig!"));
task.setOnFailed(e -> label.setText("Fehler!"));
new Thread(task).start();
Vorteile: Fortschritt, Abbruch, Ereignisse für Erfolg/Fehler. UI‑Änderungen — über sichere Methoden (updateMessage, updateProgress) oder Handler (setOnSucceeded u. a.).
4. Richtige und falsche Muster
Falsch: lange Operationen im Ereignis-Handler
button.setOnAction(e -> longOperation()); // Die UI friert ein!
Richtig: lange Operationen in einem separaten Thread
button.setOnAction(e -> new Thread(() -> longOperation()).start());
Noch besser: Task/Worker verwenden
JavaFX:
button.setOnAction(e -> {
Task<Void> task = new Task<>() {
@Override
protected Void call() throws Exception {
longOperation();
return null;
}
};
task.setOnSucceeded(ev -> label.setText("Fertig!"));
new Thread(task).start();
});
Swing:
button.addActionListener(e -> {
SwingWorker<Void, Void> worker = new SwingWorker<>() {
@Override
protected Void doInBackground() throws Exception {
longOperation();
return null;
}
@Override
protected void done() {
label.setText("Fertig!");
}
};
worker.execute();
});
5. Praxis: Beispiel für das Laden einer Datei
JavaFX:
button.setOnAction(e -> {
Task<String> task = new Task<>() {
@Override
protected String call() throws Exception {
// Simulation eines langwierigen Ladevorgangs
Thread.sleep(2000);
return "Datei geladen!";
}
};
task.setOnSucceeded(ev -> label.setText(task.getValue()));
new Thread(task).start();
});
Swing:
button.addActionListener(e -> {
SwingWorker<String, Void> worker = new SwingWorker<>() {
@Override
protected String doInBackground() throws Exception {
Thread.sleep(2000);
return "Datei geladen!";
}
@Override
protected void done() {
try {
label.setText(get());
} catch (Exception ex) {
label.setText("Fehler!");
}
}
};
worker.execute();
});
6. Typische Fehler beim Arbeiten mit dem EDT und langen Operationen
Fehler Nr. 1: Lange Operation im EDT. Die gesamte Anwendung „friert ein“, das Fenster reagiert nicht, der Benutzer denkt, das Programm sei abgestürzt.
Fehler Nr. 2: Versuch, die UI aus einem Hintergrund-Thread zu aktualisieren. Das Verletzen der Thread-Sicherheit der UI kann zu Bugs, Artefakten und Abstürzen führen. Verwenden Sie SwingUtilities.invokeLater oder Platform.runLater.
Fehler Nr. 3: Keine Fehlerbehandlung in der Hintergrundaufgabe. Ausnahmen „verschwinden“, der Benutzer weiß nicht, was schiefgelaufen ist. In Swing — überschreiben Sie done() und lesen Sie get(); in JavaFX — registrieren Sie setOnFailed.
Fehler Nr. 4: Keine Möglichkeit, die lange Operation abzubrechen. Der Benutzer kann Laden/Berechnung nicht abbrechen. Nutzen Sie die Abbruchunterstützung (SwingWorker.cancel, Task.cancel) und prüfen Sie Abbruch-Flags innerhalb der Aufgabe.
Fehler Nr. 5: Keine Fortschrittsanzeige. Der Benutzer denkt, das Programm sei „eingefroren“. In Swing — verwenden Sie Ergebnisveröffentlichung und Fortschrittsbalken zusammen mit SwingWorker; in JavaFX — updateProgress und visuelle Indikatoren.
GO TO FULL VERSION