Swing invokeLater nötig beim GUI erstellen?

Bile Demon

Bekanntes Mitglied
Hallo,

ich habe mal wieder eine kleine Frage, die hoffentlich recht schnell beantwortet ist.
Schon eine ganze Weile schaue ich im Netz nach einer Antwort, finde aber leider nichts konkretes zu meiner Frage.

In vielen Beispielen zu Swing wird eine GUI in der main-Methode so erstellt:

Java:
SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        createAndShowGUI();
    }
});

invokeLater soll ja vor allem dann benutzt werden, wenn man die GUI aus einem anderen Thread als dem EDT aktualisieren will. Aber wieso ist sowas schon beim Erstellen der GUI nötig?

Ich frage schon deshalb, weil ich bisher immer folgendermaßen meine Programme aufgebaut habe:

Java:
public static void main(String[] args){
    TestJFrame tjf = new TestJFrame();
    tjf.setVisible(true);
    doOtherStuff();
    // etc.
}

Hatte bisher nie Probleme damit. Die GUI lief fleißig nebenher, so wie sie sollte.
 
Why InvokeLater (Swing / AWT / SWT / JFace forum at JavaRanch)
javax.swing (Java Platform SE 6)

im Detail lesen bin ich nie der fleißigste,
eine Vermutung von mir: während die GUI aktiviert wird, flattern erste Events wie das erste Zeichnen ein,
wenn der main-Thread die GUI startet, kann der AWT-Thread das schon machen,
gleichzeitig, während vielleicht noch Initialisierungen laufen,

das muss keinen Fehler geben, aber kann, besonders bei größeren Programmen,
passiert alles im AWT-Thread, ist der saubere Ablauf garantiert
 
Alles klar, damit kann ich schonmal was anfangen.

Hinter den Links steht unter anderem das hier:
Exceptions to the rule: An application's GUI can often be constructed and shown in the main thread.

Java:
public class MyApplication {
    public static void main(String[] args) {
       JFrame f = new JFrame("Labels");
       // Add components to
       // the frame here...
       f.pack();
       f.show();
       // Don't do any more GUI work here...
   }
}

Das Erstellen der GUI scheint die einzige Ausnahme zu sein, wo invokeLater nicht unbedingt nötig ist. Bei jeder folgenden Änderung der GUI von außen muss man es weiterhin verwenden.

Trifft das auch dann zu, wenn ich keine Methode aufrufe, aber z.B. eine einfache Variable verändere? Also eine Änderung, die nur indirekt oder gar keine Auswirkung auf die Optik der GUI hat? Muss das dann auch im AWT-Thread passieren?
 
Hm, also fast alle Statusvariablen sind weder volatile noch synchronized. Daher sollte invokeLater immer benutzt werden.

Klar, beim Erstellen der Gui ist das nicht nötig, da kein Thread bislang dieses Objekt kennt und in sofern keine Visibilityprobleme oder Synchronizedprobleme auftreten sollten.


PS: Der AWT Thread ist genauso ein Thread wie jeder andere. Es gelten die gleichen Regeln... Synchronisierung (Eben halt invokeLater!) ist immer dann nötig, wenn 2 Thread simultan auf DAten zugreifen. Da der AWT Thread ständig drauf zugreift ist jede Änderung von außen eine Verletzung dieser Regel. Grundsätzlich würde ich invokeLater benutzen.... Es gibt auch viele Methoden die das selbst intern überprüfen.. Nach dem Motto "Ist der Thread, der das gerade ausführt ein AWT Thread ? Ja? -> Führe es aus - Nein ? -> Füg es in die Queue und führe es später im AWT Thread aus.". Generell sind wohl so Dinge wie setVisible intern bereits synchronisiert... Aber ich würde zur Sicherheit von Anfang an sauberen Code schreiben. Natürlich kannst du in Callbackmethoden (ActionListener, KeyListener, etc) direkt die Methoden aufrufen (Die Callbackmethoden werden in AWT Threads gestartet).
 
Zuletzt bearbeitet:
> Klar, beim Erstellen der Gui ist das nicht nötig

ums Erstellen geht es doch gerade..,
mich überrascht diese offizielle Exception etwas, zeigt umso mehr dass ich hier nicht mit gesicherten Wissen glänze,

Technically, the setVisible call is unsafe because the components have already been realized by the pack call. However, because the program doesn't already have a visible GUI, it's exceedingly unlikely that a paint request will occur before setVisible returns.
klingt aber auch nicht sehr vertrauenswürdig

edit:
also dass ein Fehler bei kleinen Programmen in robuster JVM (Linux, OpenJDK usw. vertraue ich nicht ganz so sehr) praktisch nie auftritt kann ich selbst bestätigen,
ich verzichte auch immer auf invokeLater in Testprogrammen,
aber das ist ja kein Grund das offiziell gutzuheißen

-----


Befehle nach setVisible(true) stehen selbstverständlich ganz außer Frage, wenn sie mit der GUI zusammenhängen,
'indirekt' wäre im Detail zu klären, klingt vorerst harmlos
 
Zuletzt bearbeitet von einem Moderator:
Bestens. Dann gewöhne ich mir künftig einfach das invokeLater beim GUI instanziieren an.

Zum Thema "indirekt":
Bei invokeLater denke ich an so offensichtliche Änderungen wie JButtons ein- oder ausblenden durch einen anderen Thread. Da wäre mir klar, dass man das dem EDT überlassen sollte.

Was ist aber wenn ich _von außen_ z.B. in einem JPanel eine Boolean-Variable setze, die darüber entscheidet, ob in paintComponent() auf dem JPanel ein bestimmter Bereich gezeichnet wird oder eben nicht. Das wäre so meine Definition von indirekt. Müsste ich dieses Setzen der Variable dann auch via invokeLater an den EDT übergeben, oder ist das in Ordnung?
 
bei einem boolean/ sonstiger recht atomarer Wechsel, z.B. einer Farbe,
könnte das Problem bestehen, dass das paint darauf mitten im Zeichenvorgang reagiert,
die untere Hälfte mit neuer Farbe malt oder neue Koordinaten, während die obere fertige Hälte noch alte Einstellungen zeigt,
(wenn man weiß wie sich die Daten auswirken, dann vielleicht weniger kritisch)

wird gar eine Liste von Daten um ein Element erweitert kann es zur klassischen ConcurrentModificationException kommen,
das Paradebeispiel für Synchronisation,

das klingt also für mich ziemlich direkt, normalerweise ein Fall für invokeLater,
der Wahrscheinkeit nach aber entsprechend nicht unbedingt,
besonders wenn man selber gut kontrolliert dass erst danach ein repaint() drankommt
 
Praktisch alle Swing-Beispiele verwenden invokeLater. Laut Threads and Swing wäre es beim Erstellen eigentlich meistens nicht notwendig (außer wenn man z.B. irgendwo
pack();
setVisible(true);
macht), aber ich habe schon L&Fs gesehen, die mit einer Exception abekachelt sind, wenn man das Erstellen ohne invokeLater macht. (Ich meine sogar, das Nimbus dafür anfällig wäre, aber habe natürlich keinen 100% reproduzierbaren Testfall dafür).

Also: Mit den 3 Zeilen mehr, die das ganze in invokeLater wickeln, ist man auf der sicheren Seite.
 
Uff, an sowas habe ich gar nicht gedacht. Da bieten (scheinbar) fehlerfrei laufende Programme wohl doch mehr Fehlerpotenzial als ich für möglich gehalten habe.

Das klingt fast so als müsste ich doch mal das eine oder andere alte Programm von mir auf solche Unpässlichkeiten hin überprüfen und korrigieren.

Vielen Dank für die Kommentare. Damit wäre das Thema für mich erledigt.
 
Vlt hast du deine alten Programme auf Singlecore Rechnern laufen gelassen 😛 ? Die physikalische Prozessoranzahl ist hier extremst entscheidend. Ich hab zu diesem Thema letztens ziemlich intensiv gegooglet.

Multithreading hat generell 3 Probleme: 1. Ordering, 2. Visibility, 3. Atomacity.

1. und 2. fallen bei Singlecore nicht auf. Visibility ist ein Problem, dass durch mehrere CPU Caches entsteht (letztens qualvoll durch meinen Info2 Prof. erfahren 😀). Ordering ist ebenfalls ein Problem, dass erst bei WIRKLICHER Parallelität auftaucht (mehrere physikalische Prozessoren).

3. Atomarität ist tatsächlich bei allen primitiven typen und Referezen (außer long, double) gegeben. Allerdings sollte man aufpassen, was genau atomar ist und was nicht. Atomar sind nur setten/getten. Inkrementieren ist keine atomare Operation. Dafür gibts die Atomic*... Typen von Java .. öhm 6 ?

Ich finde, dass das MThreading wirklich eines der komplexesten Probleme ist, vorallem wenn man jenseits von synchronized agiert und LockFree Pattern benutzen will!
 
Tatsächlich waren da am Anfang noch Singlecore-Rechner am Werk, aber nicht ausschließlich. Meine Programme werden ohnehin nie so aufwändig, dass ich echte Parallelität mit Locks bräuchte. Wird Zeit, dass so ein Fall mal eintritt 😉

Ich habe eben in einem meiner Projekte das Erstellen der GUI via invokeLater dem AWT-Thread überlassen. Prompt gab es eine NPE, weil ich direkt im Anschluss von außen auf Komponenten der GUI zugreifen wollte, der AWT-Thread mit dem Aufbauen der GUI aber noch gar nicht fertig war. Vorher ging das natürlich problemlos weil nicht parallel.

Als Workaround habe ich zuerst eine Wartezeit von 100 ms eingebaut - danach lief es wieder wie gewohnt. Dann ist mir eingefallen, dass es ja noch invokeAndWait gibt. Jetzt weiß ich endlich wofür das da ist 😀
 
Zuletzt bearbeitet:

Zurück
Oben