Ja, das DnD-Thema liegt immer noch als „Noch nicht so ganz abgeschlossen“ in meinem Gewissen. Vom „Wie man aus Applets eine Anwendung macht“-Wiki-Eintrag mal ganz zu schweigen 
Die Methode, in der der Code implementiert ist, wird als Reaktion auf die Selektion eines JLabels, das dynamisch (zur Laufzeit) auf dieses Panel gesetzt wurde, aufgerufen.
Wenn das, was da gemacht wird, als „Reaktion“ auf irgendeine GUI-Aktion passiert, dann läuft es ja schon auf dem EDT. Wenn man z.B. irgendwo einen ActionListener
oder MouseListener
oder so dranhängt, dann wird alles, was von der jeweiligen actionPerformed
/mousePressed
usw. gemacht und aufgerufen wird, vom EDT ausgeführt.
In der „reinen“ GUI-Welt braucht man SwingUtilities.invokeLater
darum eigentlich recht selten.
Der Punkt an dieser Stelle ist, dass „irgendein“ Thread das getFont
aufruft. Und an irgendeiner anderen Stelle könnte ein anderer Thread (der EDT, oder ein ganz anderer) im gleichen Moment setFont
aufrufen. Welcher Font dann tatsächlich bei getFont
zurückkommt, weiß man erstmal nicht. Im schlimmsten Fall kann das dann halt mit „unerklärlichen“ Fehlern abkacheln oder sich beliebig komisch verhalten. Das setFont
ist zwar noch recht einfach (vielleicht wirklich nur ein setter/getter), aber das weiß man ja erstmal nicht…
Allgemein:
Der Punkt, wo man SwingUtilities.invokeLatern
braucht, ist: Wenn man irgendwas mit eigenen Threads macht. Kürzlich hatte ich da auch mal einen (kurzen) Primer in einer Antwort geschrieben: java - Starting threads from inside EDT event handler code in Swing apps - Stack Overflow
Der relevante Teil nochmal kurz: Wenn man sowas macht
someButton.addActionListener(e -> {
doSomeComputationThatTakesFiveMinutes();
someLabel.setText("Finished");
});
Dann wird die doSomeComputationThatTakesFiveMinutes
-Methode, wie oben schon erwähnt, vom EDT ausgeführt - der damit 5 Minuten lang blockiert ist. Das ist nicht gut. Deswegen könnte man das auf einen anderen Thread legen:
someButton.addActionListener(e -> {
Thread thread = new Thread(() -> {
doSomeComputationThatTakesFiveMinutes();
someLabel.setText("Finished");
});
thread.start();
});
Aber: Das ist genau der Punkt, wo man aufpassen muss. Dieser Thread würde ja auch das someLabel.setText
ausführen. D.h. dort würde eine GUI-Komponente von einem Thread verändert, der nicht der EDT ist.
Um das „sauber“ zu machen, müßte man diesen Teil dann wieder in den EDT zurückreichen, und das ist der Punkt, wo man SwingUtilities.invokeLater
verwenden muss:
someButton.addActionListener(e -> {
Thread thread = new Thread(() -> {
// Das passiert im Hintergrundthread
doSomeComputationThatTakesFiveMinutes();
SwingUtilities.invokeLater(() -> {
// Das wird dann wieder auf dem EDT gemacht:
someLabel.setText("Finished");
});
});
thread.start();
});
Wie in der Stackoverflow-Antwort auch gesagt: Das kann etwas frickelig und unbequem werden. Für genau dieses Muster gibt es dann den SwingWorker
, der das ganze etwas einfacher macht (ohne new Thread
und ohne „sichtbares“ invokeLater
). Aber wenn man da dann noch einen Dialog mit Fortschrittsbalken und Cancel-Button haben will, wird das auch wieder kompliziert. Dafür hatte ich dann mal GitHub - javagl/SwingTasks: Utility classes for task execution in Swing erstellt
SwingTaskExecutors.create(
() -> berechneZehnMinutenLangEinErgebnis(),
ergebnis -> zeigDasImGuiAn(ergebnis)).
build().execute();
Und der SwingTask kümmert sich um Threads, Dialog, und den ganzen anderen Mist.
Aber wie gesagt: Wenn man keine eigenen Threads startet, und keine Daten aus „Fremd-Threads“ ins GUI bringen will, läuft „praktisch alles“ sowieso schon auf dem EDT.
Es kann fiese Grenzfälle geben. Ein sehr vereinfachtes Beispiel:
void addRowTo(DefaultTableModel model) {
Object rowData[] = computeSomething();
model.insertRow(rowData);
}
Dort soll eine Zeile zu einem TableModel
hinzugefügt werden. Die computeSomething
-Methode rechnet vielleicht sehr lange. Darf man das in einem eigenen Thread machen, oder muss das auf dem EDT gemacht werden?
Die Antwort ist: Es kommt darauf an. Wenn es eine JTable
gibt, die das TableModel
enthält, dann muss die Änderung auf dem EDT gemacht werden. Wenn das TableModel
aber „frisch“ ist und noch nicht in einer JTable
liegt, dann hat es nichts mit GUI-Kompnenten zu tun, und kann auch in einem Fremd-Thread verändert werden…