Nur ein Thread - Ist Threadsafe Berücksichtigung wichtig?

Status
Nicht offen für weitere Antworten.

X5-599

Top Contributor
Guten Tag alle zusammen,

hier nun ein Thread Frage: In meinem Projekt habe ich eigentlich nur einen extra Thread, der die Arbeit macht und per Controller (MVP Pattern) die GUI aktualisiert.

In diesem Szenario:
Ist es da wichtig die eigentlichen Änderungen (setText() etc, der JComponents im View) per SwingUtilities.invokeLater( ... ) zu kapseln? Ich weiss, dass bis auf ein paar Ausnahmen Swing nicht Threadsafe ist. Also müsste man verhindern, dass zwei Threads gleichzeitig auf einen setter zugreifen.
Muss man diese Vorkehrung auch noch treffen, wenn es nur einen Thread gibt, der einen solchen Zugriff macht?

Ich hab es mal mit beiden Varianten implementiert. Also mit den SwingUtilities und ohne. --> Kein Unterschied in der Ausführung des Programms.
Darum eben diese Frage. Ist die SwingUtilities Sache zwingend nötig? Oder gehört das "nur" zum guten Ton?


Gruß,
Michael
 
Was heißt jetzt ein "extra Thread"? Hast du einen eigenen Thread oder nicht? Wenn ja, sind das neben dem EDT zwei Threads und du musst dich um Threadsicherherit bzw. Synchronisation kümmen.
Wenn alles im EDT passiert, brauchst du das nicht. Nur kann es dann passieren, dass dein Programm bei lang laufenden Aktionen scheinbar einfriert. Und das ist kein guter Stil.
 
Also als ich meinte: einen extra Thread meinte ich einen "eigenen". Den EDT hab ich da absichtlich ausgelassen. War vielleicht etwas verwirrend.

Es läuft also nicht alles im EDT ab(sollen ja auch fortschritte zu sehen sein...) Du sagst also: insgesamt zwei Threads --> darum Thread Sicherheit herstellen. Aber doch nur weil per GUI(klick-Events) noch auf die GUI Komponenten zugegriffen werden könnte, oder?

Ich habe allerdings sobald der Thread(Arbeiter) loslegt, alle Komponenten der GUI(Buttons, ComboBoxes), die Events auslösen könnten, disabled. Während das Programm läuft, sieht man die Fortschritte in einer GUI TextField-Komponente (Es tritt kein Einfriereffekt ein)

Es wird während des Arbeitsablaufes wirklich nur von meinem eigenen Arbeiter-Thread aus auf die GUI Komponenten zugegriffen(Aktualisierung des Fortschrittes). So wie ich das sehe, ist von woanders her, keine Beeinflussung möglich... Liege ich da richtig?

Und falls ja, wieder die Ursprüngliche Frage: Ist es dann unbedingt notwendig "Threadsafe" zu arbeiten?

Übrigens, Nein, ich sträube mich nicht Threadsafe zu arbeiten. Ich möchte es nur ersteinmal verstanden haben, ab wann es überhaupt erforderlich wird.
Denn soweit wie ich es jetzt verstehe, ist folgende Ausage zwar nicht falsch(im Sinne von nicht kompillierbar), aber auch richt 100%ig richtig:
"Ich habe selbst einen Thread erstellt und MUSS darum Threadsafe mit Swing Komponenten arbeiten."

Es sei denn:
Frage: Ist die Threadsicherheit nur bei setter Zugriffen nötig? Oder kann es auch zu Problemen kommen, wenn zwei Threads gleichzeitig (einer per "get") auf eine Komponente zugreifen wollen?
 
Moin,

MSDN lesen ... die haben das schön beschrieben - grobe Zusammenfassung

"arbeitet ein Thread bei einem Kontext-Wechsel mit einem Objekt und kann es bei diesem Wechsel in einem inkonsitentem Zustand zurück lassen, dann ist für Threadsicherheit zu sorgen"

sprich kann eine Thread ein Object zerstören wenn es damit nicht fertig wird, dann sorge dafür das der Thread damit fertig werden darf ... auch wenn ACID aus der Entwicklung von Datenbanken stammt - so hat es seine Gültigkeit

hand, mogel
 
Um das nochmal deutlich zu sagen:
Wenn es heißt das Swing nicht threadsicher ist, und das man synchronisieren muss, dann meint das nicht den Fall das du zwei Worker Threads hast die gleichzeitig die Oberfläche manipulieren.
Kein Worker Thread darf die GUI manipulieren, das darf nur der EDT. Daher muss alles was nicht explizit als threadsafe markiert ist per invokeLater ausgeführt werden.
 
@Wildcard

meinst du jetzt per invokeLater() ausführen, aus den Threads heraus? also in einer run() Methode swingKomponente.invokeLater( ... ) aufrufen?

das würde natürlich schon Sinn machen. Allerdings muss ich jetzt wieder gestehen, dass ich sowas gar nicht mache. Im Thread sind nämlich gar keine GUI Komponenten bekannt. Nur der Controller. Und der befindet sich doch im EDT, nicht wahr?
 
Objekte gehören niemanden,
wenn ein Thread x eine Methode eines Controller-Objektes ausführt, dann passiert das so wie man es sich denken kann innerhalb des Threads x,
da wird nirgendwo gewechselt,
wenn ein Thread x einen ActionListener kennen würde und dort selber actionPerformed() aufrufed, dann wird das auch von x ausgeführt, obwohl ja normalweise ActionListener im EDT laufen,
aber nicht, weil es ein ActionListener ist, sondern weil eben normalerweise nur der EDT Ereignisse verarbeitet und actionPerformed() aufruft,
Objekte und Methoden haben an sich nix zu sagen, es kommt immer drauf an, welcher Thread sie aufruft/ benutzt
 
@SlaterB, @Wildcard

Danke euch. Habt mein ganzes bisheriges (Halb)Wissen dieses Themas gelöscht! 😉 Und das ist auch gut so! Was will man schon mit absolut falschem Wissen anfangen?!

Der SwingUtilities.invokeLater() Befehl registriert also Aufgaben(wenn man so will) von "Ausserhalb" beim EDT in einer Art Queue. Weil der EDT der einzige Thread ist, der die GUI verändern darf.

So, nun weiter:
Einfaches Beispiel:

Zwei Arbeiter-Threads kennen die gleiche GUI-Komponente(JLabel):
Beide müssten ihre beabsichtigte Änderung (setText) also per SwingUtilities.invokeLater() beim EDT queue'en, da setText nicht threadsicher ist und die Threads halt von ausserhalb des EDT agieren. Und der EDT arbeitet diese dann ab, sobald er Zeit dazu hat.
Bis hierhin richtig?

Speziele Frage: Wie ist das mit threadsicheren Methoden wie z.B. appendText() von der JTextArea? Wie ich Wildcard verstanden habe, bräuchte man dafür kein invokeLater(), da appendText() threadsicher ist. Aber der Aufruf käme dann doch immernoch von ausserhalb des EDT?? *kopfkraz*

Ist das dann eine Ausnahme der Regel: Nur der EDT darf die GUI verändern?
Also sowas wie: invokeLater() nur wenn von ausserhalb des EDT U N D die aufzurufende Methode nicht threadsicher ist?


Ist da etwas brauchbares dabei, oder bringe ich wieder vieles durcheinander? Ich denke(hoffe) aber mal, dass ich langsam auf dem richtigen Weg bin...

Gruß,
Michael
 
alles ziemlich richtig, 'threadsicher' trotz 'ausserhalb des EDT',
ohne diese Besonderheit müsste man es ja nicht 'threadsicher' nennen 😉

aber man muss nun auch nicht für jede Methode nachdenken, dann ist es vielleicht einfacher, alles in invokeLater() auszuführen,
es bietet sich ja sowieso an, mehrere Kommandos zu bündeln,

hat man nur wenige/ einzelne, und weiß, dass es threadsicher ist, dann kann man vielleicht drauf verzichten
 
Danke,

ich denke jetzt hab ich es. Der Leitsatz: "Der EDT ist der einzige Thread der die GUI verändern darf", ist also nur guter Stil aber keine Regel(nach dem Motto: Was anderes würde nicht funktionieren)

Es ist also wohl so, dass die meisten Methoden von Swing-Komponenten nicht Threadsicher sind und deshalb per Queue hintereinander abgearbeitet werden sollen, damit es keine unerwarteten Effekte gibt. Und das realisiert man mit invokeLater().
Bei den wenigen threadsicheren Swing-Methoden bräuchte man keine solche Queue, da diese Methoden bereits ihre "eigene" Queue mitbringen.

Korrekt?
 
'ist also nur guter Stil aber keine Regel' kannst du ja wohl nicht nur deswegen sagen, weil einige Methoden threadsicher sind?

es gilt zwar auch für alle anderen: meist klappst auch ohne invokeLater(), besonders in einfachen Programmen,
aber das kann man dann nicht als Stil auslegen, sondern als unsauberes Spiel mit dem Feuer ohne Netz und doppelten Boden
 
Was passiert wenn die GUI nicht vom EDT manipuliert wird ist nicht definiert.
Kann gut gehen, kann sich in seltsamer Anzeige äussern, kann zum Absturz führen, kann zum Deadlock führen.
In jedem Fall wirst du große Probleme haben die eigentlich ursächliche Stelle später noch zuordnen zu können. Also nein, es ist nicht guter Stil, sondern eine Regel.
 
Moin,

aber das kann man dann nicht als Stil auslegen, sondern als unsauberes Spiel mit dem Feuer ohne Netz und doppelten Boden

ACK ... das Problem ist das Betriebssystem und eine Java-Programm weis eigentlich nicht auf welchem BS es läuft (gut - man kann es abfragen) ... aber jedes BS hat andere Macken ... in dem Sun einfach sagt "macht es mit dem EDT", kann Sun nach Bedarf auf Änderungen in einem BS reagieren OHNE das es Probleme mit Deinem Programm gibt ... im Moment ist es machbar auch ohne EDT setText() aufzurufen ... aber was wird unter Windows 7 oder gar später passieren?

im .NET Framework ist das richtig hart geregelt ... da fliegt einfach eine Exception bei Zugriff von einem Thread auf die GUI ... allerdings erst ab 2.0 unter 1.1-- gab es die Exception nur wenn das Framework Lust & Laune dazu hatte 😀

hand, mogel
 
Danke euch,

ok, also doch eine Regel. Gut dass ich's jetzt weiss.
Ich habe alle GUI Interaktionen nun mittels invokeLater() gemacht. Eines bereitet mir aber trotzdem noch Kopfzerbrechen:
wie läuft das ganze denn ab, mit einem MVP Pattern? Ich meine, das invokeLater() bezieht seine Daten, die in der GUI aktualisiert werden, ja aus dem Model. Wann allerdings das entsprechende get() aufgerufen wird ist aber nicht bekannt. Das Runnable steht ja in der Queue...
Jetzt könnte es ja passieren, daß die Abarbeitung der Queue aus irgendeinem Grund verzögert wird und derselbe Thread wieder eine GUI Änderung per invokeLater() aufruft. -Sicher, der Auftrag wird hinter den ersten eingereiht, aber das set() des Models hat ja vor dem invokeLater() stattgefunden. das hiesse das Model hat zweimal (für beide Aufträge) seinen Wert geändert, aber die eigentliche Änderung der GUI (Abarbeitung des ersten Auftrages) hat noch gar nicht stattgefunden.

Sehr, theoretisch, ich weiss. Aber sowas könnte doch passieren, oder?
Wie würde man sich davor schützen?
Meine Antwort wäre den zu ändernden Wert des Models mit in den invokeLater() Auftrag zu stecken. Aber das geht ja net, da bei MVC erst das Model geändert wird und erst dann der notify() den Auftrag schreibt...

Irgendwie denke ich, ich bin wieder auf dem Holzweg(und wohl nicht zu knapp)
ich meine: Es funktioniert ja, so wie ich es jetzt habe. Ist ja eh nur ein EDT und ein Worker Thread, die sich eigentlich nicht in die Quere kommen können. Aber ganz wohl ist mir bei der Sache nicht. Das schlimmste was passieren könnte(in oben genannten Szenario) wäre eine oder mehrere "verschluckte" Zeilen in der Anzeige.
Ist sicherlich nicht sooo schlimm. Aber ich meine bei so einem noch recht simplen Programm sollten solche Fehler GARNICHT auftreten können.
Und wie gesagt, mich beschleicht das Gefühl, dass diese Situation nur dadurch zu Stande kommt, weil ich eine Wichtige Kleinigkeit in Sachen Threadsafety und MVC Pattern noch nicht gerafft habe...
 
Ich bin nicht ganz sicher, ob ich das Problem wirklich verstanden habe. Wo wäre das Problem dabei, wenn sich die Darstellung einmal nicht mitändern würde? Wenn die EventQueue nicht regelmäßig abgearbeitet wird, sieht man überhaupt keine Änderungen in der GUI. Kein Button lässt sich drücken. Kein Fokus wird gewechselt. Das Fenster wird nicht neu gezeichnet, wenn man es vergrößert.

Wenn es nötig ist, zu warten, bis der EDT einen Request abgearbeitet hat, nimmt man SwingUtilities.invokeAndWait(Runnable). Allerdings ist das eher die Ausnahme, anderenfalls ein Indikator für ein Verständnisproblem.

Ebenius
 
also ich meine:
Angenommen die Eventqueue der invokeLater Runnables steht kurze Zeit. Und ein Thread gibt direkt hintereinander zwei gui Änderungsaufträge ab.
also: auftrag1 - setneuenWertInModel("Wert von Auftrag1") --> setChanged() --> notifyObservers().
in update() des Observers wird mit invokeLater(new Runnable( public void run( model.getWert() ) ) ) der Auftrag gebaut. das heisst doch, dass die Methode model.getWert() erst dann aufgerufen wird, wenn der Auftrag abgearbeitet wird, oder nicht?
Und wenn wie gesagt, die Queue steht und der Thread nun direkt hinter dem ersten Auftrag einen zweiten abgibt: auftrag2 - setneuenWertInModel("Wert von Auftrag2") --> setChanged() --> notifyObservers() etc. ist doch durch setneuenWertInModel() das Feld des Models geändert worden, bevor der erste Auftrag seine Änderung mit getWert() darstellen konnte.

Also würde, sobald die Queue wieder anläuft zweimal der "Wert von Auftrag2" dargestellt werden.

Ich wüsste nicht wie ich es besser erklären sollte 🙁
 
Achso. Da hast Du wahrscheinlich einen Denkfehler. Der normale Fall lautet doch: [Highlight=Java]SwingUtilities.invokeLater(new Runnable() {
model.setValue(someValue); // set the value in the model.
}[/Highlight]
Das Model sendet dann den Event (bzw. benachrichtigt den Observer), wenn sich die Daten ändern. Man setzt nicht den Wert des Modells und lagert die Benachrichtigung aus. Man lagert das Setzen des Wertes im Modell aus. Damit ist alles synchron.

Ebenius
 
Da hab ich keine Ahnung wie es jetzt gemacht wird. ich hab mich an die FAQs gehalten ^^

zu sehen hier: http://www.java-forum.org/51799-post11.html

Da ist der Controller das "Observable" ... nicht das "Model". Gut ist kein Swing Beispiel. Aber trotzdem, die Vorgehensweise ist doch eher so wie ich es gemacht habe: Controller ändert Wert im Model, setzt seinen Status auf <changed> und benachrichtigt den Observer(die View) Dabei wird dem Observer das geänderte Model übergeben.

Wie ist es jetzt richtig? Oder gibt es da keine "genormte" Vorgehensweise?
 
*threadrauskram*

hallo,

aus aktuellen anlass bin ich wieder auf das thema mvc gekommen. sind die faqs hier nun falsch, oder wie?
stichwort model veränderung und setChanged() aufruf...

danke,
michael
 
aha,
danke schön für die info.

gehe ich richtig in der annahme, dass also das model der Observable sein sollte? (imho machte das auch am meisten sinn. schon von namen her...)

gruß,
michael

p.s. falls das jemand der ordnung halber in den faqs berichtigen könnte ... wär' ganz toll. macht nicht soviel spass sich an die regeln des guten tons zu halten und erst in die faqs zu sehen bevor man postet und dann etwas "falsches" zu implementieren(so wie ich ;D )
 
gehe ich richtig in der annahme, dass also das model der Observable sein sollte? (imho machte das auch am meisten sinn. schon von namen her...)
Jupp. Das Modell ist Observable. Die Präsentation (View) hört dem Modell zu und aktualisiert sich automatisch. Die Steuerung (Controller) nimmt Eingaben von einer oder meheren Präsentation(en) entgegen und reagiert entsprechend. Wenn sich aus diesen Reaktionen Änderungen im Modell ergeben, reagiert die Präsentation (als Observer des Modells) automatisch. Die Steuerung kann -- je nach Anwendungsfall -- ebenfalls Observer eines Modells sein und die Präsentation manipulieren.

p.s. falls das jemand der ordnung halber in den faqs berichtigen könnte ... wär' ganz toll. macht nicht soviel spass sich an die regeln des guten tons zu halten und erst in die faqs zu sehen bevor man postet und dann etwas "falsches" zu implementieren(so wie ich ;D )
Stimmt, sollte mal erledigt werden. Der Beitrag ist ohnehin am falschen Ort, da MVC kein Entwurfsmuster (Design Pattern), sondern ein Architekturmuster (Architectural Pattern) ist.

Ebenius
 
Zuletzt bearbeitet:
Status
Nicht offen für weitere Antworten.

Zurück
Oben