PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [Java] Brauche wohl zusätzliche Threads, wie aufbauen?


lima~
2010-04-17, 19:14:41
Hallo!

Das Problem ist eigentlich schnell erklärt:


Ich habe eine Klasse X, welche eine Hashmap beinhaltet/verwaltet.
Der Inhalt der Hashmap soll in einem bestimmten Rhytmus mit einer Quelle (XML) im Internet synchronisiert (aktualisiert) werden. Die Einträge der Hashmap entsprechen den Einträgen der XML (diese hat nur eine "Ebene")
Die Klasse X besitzt, von einem Interface vorgeschrieben, die Methode Update() - in dieser soll die Aktualisierung stattfinden


Problem: das Laden der XML als auch die anschließende Verarbeitung (herausfinden, welche Einträge neu sind etc.) benötigen ja Zeit, weshalb hier wohl mit einem neuen Thread gearbeitet werden sollte - schielßlich wäre es unschön, wenn das GUI deswegen hängen bleibt.

Leider habe ich noch nie mit Threads gearbeitet und benötige etwas Hilfe.

Klappt das überhaupt so, dass nur die Methode Update() ihre Arbeit intern auf einen anderen Thread auslagert, oder muss da zwingend die ganze Klasse geändert werden?

Im Endeffekt soll ja durch die Methode die Hashmap der Klasse manipuliert werden. Was ist die bessere Methode, bzw. was ist überhaupt möglich: die Hashmap der Update-Methode übergeben und verändern lassen (da Referenz), oder von der Update-Methode eine neue Hashmap zurückliefern lassen und somit die alte überschreiben?

Was auch noch bedacht werden muss: in der Update-Methode können ja Exceptions auftreten. Z.B. eine IOexception, wenn die XML-URL nicht existiert o.ä. Das würde ich gerne nach draußen weiterleiten.

Threads werden ja immer als fehleranfällig und risikobehaftet beschrieben. Sehe ich das richtig, dass ich davon vermutlich verschont bleibe? Schließlich hat jede Instanz von X seine eigene XML-Adresse (und greift ja sowieso nur lesend drauf zu, also egal), desweiteren gibt es pro Objekt nur eine Methode, die versucht die Hashmap zu ändern: die eigene Update-Methode.


Ich weiß jetzt garnicht, ob das klassische Java-Threading dafür überhaupt in Frage kommt, oder ob ich da eine dieser Neuerungen brauche: Callable, Future, Executioner - irgendwas davon vielleicht. Zum Beispiel wegen der Rückgabe der Exceptions etc. Aber bei den ganzen Neuerungen fehlt mir echt der Überblick.

Wäre um etwas Hilfe dankbar! Grüße

Yavion
2010-04-18, 00:30:58
Hallo!

Das Problem ist eigentlich schnell erklärt:


Ich habe eine Klasse X, welche eine Hashmap beinhaltet/verwaltet.
Der Inhalt der Hashmap soll in einem bestimmten Rhytmus mit einer Quelle (XML) im Internet synchronisiert (aktualisiert) werden. Die Einträge der Hashmap entsprechen den Einträgen der XML (diese hat nur eine "Ebene")
Die Klasse X besitzt, von einem Interface vorgeschrieben, die Methode Update() - in dieser soll die Aktualisierung stattfinden


Problem: das Laden der XML als auch die anschließende Verarbeitung (herausfinden, welche Einträge neu sind etc.) benötigen ja Zeit, weshalb hier wohl mit einem neuen Thread gearbeitet werden sollte - schielßlich wäre es unschön, wenn das GUI deswegen hängen bleibt.

Leider habe ich noch nie mit Threads gearbeitet und benötige etwas Hilfe.

Klappt das überhaupt so, dass nur die Methode Update() ihre Arbeit intern auf einen anderen Thread auslagert, oder muss da zwingend die ganze Klasse geändert werden?

Im Endeffekt soll ja durch die Methode die Hashmap der Klasse manipuliert werden. Was ist die bessere Methode, bzw. was ist überhaupt möglich: die Hashmap der Update-Methode übergeben und verändern lassen (da Referenz), oder von der Update-Methode eine neue Hashmap zurückliefern lassen und somit die alte überschreiben?

Was auch noch bedacht werden muss: in der Update-Methode können ja Exceptions auftreten. Z.B. eine IOexception, wenn die XML-URL nicht existiert o.ä. Das würde ich gerne nach draußen weiterleiten.

Threads werden ja immer als fehleranfällig und risikobehaftet beschrieben. Sehe ich das richtig, dass ich davon vermutlich verschont bleibe? Schließlich hat jede Instanz von X seine eigene XML-Adresse (und greift ja sowieso nur lesend drauf zu, also egal), desweiteren gibt es pro Objekt nur eine Methode, die versucht die Hashmap zu ändern: die eigene Update-Methode.


Ich weiß jetzt garnicht, ob das klassische Java-Threading dafür überhaupt in Frage kommt, oder ob ich da eine dieser Neuerungen brauche: Callable, Future, Executioner - irgendwas davon vielleicht. Zum Beispiel wegen der Rückgabe der Exceptions etc. Aber bei den ganzen Neuerungen fehlt mir echt der Überblick.

Wäre um etwas Hilfe dankbar! Grüße

Also ich bin jetzt nicht so Firm in Java GUI Frameworks. Aber:
Du solltest die zeitaufwändige update() Methode von der GUI trennen. Das kannst Du mit Threads tun. Da man Threads in Java m.E. immer in Zusammenhang mit Objekten erstellt, bietet es sich an, den Thread im Konstruktor deiner Klasse zu erstellen, die die Methode update() beinhaltet. So würde ein NEW Aufruf automatisch nicht nur eine neue Instanz sondern gleich einen eigenen Thread generieren. D.H. deine update() liefe sofort in einem neuen Thread ab.
Der kritische Teil ist, wie du aus diesem neuen Thread jetzt wieder in deine GUI schreiben kannst, was anscheinend erwünscht ist. Da bin ich mir wie gesagt nicht sicher: In .NET gibt es zwingendermaßen sowas wie invoke oder begininvoke, damit der Zustand der Anzeigelemente konsistent bleiben.

Was die Updade() Methode angeht: ich würde mir eine neue Hashmap zurückgeben lassen und nicht über Seiteneffekte eine Instanzhashmap manipulieren. Letzteres halte ich besonders bei Multithreading für ungünstig.

Monger
2010-04-18, 01:02:53
Es gibt in Java auch eine Art von Worker Thread Klasse... in Swing wäre das dieser hier (http://java.sun.com/javase/6/docs/api/javax/swing/SwingWorker.html). Der hat den großen Vorteil, dass er Ereignisse an die GUI absetzen kann... wenn man das selbe mit der Thread Klasse versuchen würde, kann das ganz schön knifflig werden.

Multithreading ist halt so eine Sache. Wenn ich dich richtig verstehe, muss die HashMap auch von mehreren Threads aus geschrieben und gelesen werden. Die normale HashMap ist allerdings nicht threadsicher, Java kennt da spezielle Implementierungen (SychronizedMap, oder so ähnlich), die das können...

Du ersparst dir viel Stress, wenn du die Aufgabe die der Thread erledigen soll so gut es geht irgendwie vom Rest isolierst, und genau dem folgst was der Swing Worker dir vorgibt. Sprich: du solltest deine Berechnungen im SwingWorker vornehmen, und dann in der "Done" Methode deine Ergebnisse in deine Datenhaltung oder deine GUI übertragen.

lima~
2010-04-19, 12:13:45
Danke schonmal euch beiden.


Was die Updade() Methode angeht: ich würde mir eine neue Hashmap zurückgeben lassen und nicht über Seiteneffekte eine Instanzhashmap manipulieren. Letzteres halte ich besonders bei Multithreading für ungünstig.

Habe ich mir auch gedacht, aber ich habe bisher keine Idee, inwiefern sich das umsetzen lässt. Denn:

Irgendwo im Code muss ja mal stehen, hashmap = update() oder sowas in diese Richting, denn irgendwie muss die Instanzhashmap ja an die Rückgabe der neuen Map kommen. Doch selbst wenn jetzt Update() in einem anderen Thread läuft, wurde das GUI ja blockieren aufgrund der einfachen Zuweisung hashmap = update(), welche ja erst dann abgeschlossen ist, wenn update ausgeführt wurde - egal in welchem Thread.

Erfreut habe ich dann die Schnittstelle callable sowie Future gefunden. Callable Klassen können einen ganz normalen Rückgabewert haben (bzw. deren call-Methode, auf die der neue Thread anspringt), auch wenn sie in einem anderen Thread laufen. Diese Rückgabe kann man einem Future-Objekt zuweisen und mit isDone() Fragen, ob das Ergebnis schon da ist. Wenn das der Fall ist, könnte man einfach hashmap = future.get() aufrufen. Wenn das Ergebnis noch nicht da ist, blockiert natürlich auch diese Zuweisung bis der Vorgang abgeschlossen ist.

Eventuell ließe sich daraus was bauen, ich bin mir nur noch nicht ganz sicher, wie.


Es gibt in Java auch eine Art von Worker Thread Klasse... in Swing wäre das dieser hier (http://java.sun.com/javase/6/docs/api/javax/swing/SwingWorker.html). Der hat den großen Vorteil, dass er Ereignisse an die GUI absetzen kann... wenn man das selbe mit der Thread Klasse versuchen würde, kann das ganz schön knifflig werden.

Danke, das werde ich mir mal ansehen. Ich dachte nur, wenn das ganze Update-Zeug eigentlich zu einer Logikklasse gehört, dann müsste man das ja so bauen, dass es unabhängig vom GUI funktioniert. Habe zwar schonmal kurz was von dem Swing-Wokrer gehört, hätte aber vermutet, dass dann das ganze zu Stark mit Swing gekoppelt wird und meine Logikklassen ohne Swing nicht mehr funktionieren würden.


Multithreading ist halt so eine Sache. Wenn ich dich richtig verstehe, muss die HashMap auch von mehreren Threads aus geschrieben und gelesen werden. Die normale HashMap ist allerdings nicht threadsicher, Java kennt da spezielle Implementierungen (SychronizedMap, oder so ähnlich), die das können...


Also jedes Objekt besitzt seine Hashmap-Instanz, und in diese sollte dann das Ergebnis der Update()-Methode einwirken, falls ein Update stattfindet. D.h. schreibend greift eigentlich nur diese eine Methode zu.


Du ersparst dir viel Stress, wenn du die Aufgabe die der Thread erledigen soll so gut es geht irgendwie vom Rest isolierst, und genau dem folgst was der Swing Worker dir vorgibt. Sprich: du solltest deine Berechnungen im SwingWorker vornehmen, und dann in der "Done" Methode deine Ergebnisse in deine Datenhaltung oder deine GUI übertragen.


Ich glaube, ich muss erst noch versuchen zu klären, wie das mit dem GUI später funktionieren soll - dachte erst das wär das einfachste, aber lag da wohl falsch. Ich skizziers mal kurz:

Es gibt einen JTree, eine JTable sowie ein Textfeld zur Ausgabe. Im JTree sollen nun die ganzen Objekte aufgelistet werden, die intern diese hashmap besitzen und Updaten können. Klicke ich nun ein solch ein konkretes Objekt an, dann sollen im JTable die einzelnen Einträge der Hashmap des angeklickten Objekts gelistet werden. Jeder Eintrag der Hashmap besitzt übrigens eine getText()-Methode, und dieser Text soll dann im Textfeld ausgegeben werden.

Analoges Beispiel: Stell es dir einfach so vor wie ein Mailclient. Erst wählst du in einer Art JTree deinen gewünschten Ordner aus (Ungelesen, Gelesen, Spam etc). Danach siehst du dann in einer Table alle Mails dieses Ordners gelistet. Klickst du auf eine Mail, siehst du in einem Textfeld ihren Inhalt.

Mein Vorgehen war naiv geplant:

Eine JTable kann ja ein zweidimensionales String-Array aufnehmen. Ich dachte, ich gebe meinen Objekten einfach eine ListItems-Methode, welche aus der Hashmap ein solches Array baut und an den JTree zurückliefert. Sobald die Update-Methode eine neue Hashmap liefert, wird die ListItems Methode ja auch ein neues Array zurückliefern.
Wie das GUI dabei aber automatisch checkt, dass es eine neue Table gibt zum Anzeigen, darüber habe ich mir naiverweise ehrlich gesagt noch gar keine Gedanken gemacht.

Warum ich das alles erzähle? Naja, wenn mein Ansatz komplett falsch ist, dann wäre eventuell jetzt der richtige Zeitpunkt für eine Änderung ;D


Grüße


edit: Noch kurz zur Erklärung was das mit der Hashmap soll: deren Key lässt sich ja als ID sehen - was für die Update-Methode hilfreich ist. Neue potentielle Einträge haben nämlich auch so einen eindeutigen Key, und wenn der nicht in der hashmap steckt, dann muss der Eintrag, bezogen auf das was schon in der Map steckt, neu sein und kann hinzugefügt werden.

Dr.Doom
2010-04-19, 13:24:03
schielßlich wäre es unschön, wenn das GUI deswegen hängen bleibt.Ohne nun genau nachvollziehen zu können, was das ganze JTree&Co-Zeugs machen soll:
Einfach in der update-Methode einen Thread erstellen und in der run()-Methode die Lade- und Datenaufbereitungsarbeit erledigen. Dann machst du noch einen Fortschrittsbalken oder ein Fenster mit wichtig blinkendem "Please be patiened, while processing incoming data...", damit der User eine Rückmeldung hat zu dem, was da gerade passiert. ;)
Wenn man aber während der Datenübertragungs- und Aufbereitungsphase was anderes mit der Anwendung machen soll, dann wieder alles in die run()-Methode packen, eine neue Hashmap erstellen lassen und wenn diese fix und fertig zur Benutzung taugt, die Anwendung/Klasse X "notifien", dass die neue Hashmap zur Abholung bereitsteht.

Ganon
2010-04-19, 13:34:25
Danke, das werde ich mir mal ansehen. Ich dachte nur, wenn das ganze Update-Zeug eigentlich zu einer Logikklasse gehört, dann müsste man das ja so bauen, dass es unabhängig vom GUI funktioniert. Habe zwar schonmal kurz was von dem Swing-Wokrer gehört, hätte aber vermutet, dass dann das ganze zu Stark mit Swing gekoppelt wird und meine Logikklassen ohne Swing nicht mehr funktionieren würden.

Du sollst den SwingWorker ja nicht in deine Logik bauen.

z.B. beim Schalter.
Du klickst drauf, baust da den SwingWorker, welcher die Logik aufruft und wenn er fertig ist, wird die GUI aktulisiert. Nichts was da in die Logik muss.

Man muss aber hier immer auf aufpassen, dass man dann sämtliche ggf. störende Aktionen unterbindet. Denn wenn ein SwingWorker läuft ist es ja Sinn, dass die GUI nicht blockiert wird. Aber man sollte dann aufpassen, dass wenn du gerade eine komplizierte Logik auf einem Datensatz laufen hast, du dem Nutzer nicht erlaubst jetzt mal eben auf Löschen zu klicken, oder das er mal eben den Datensatz wechselt ;)

Gast
2010-04-19, 14:13:44
Achso ne, ich meinte das anders. Ich dachte anfangs es wäre cool, wenn die Threadarbeit von außerhalb der Logikklasse garnicht ersichtlich ist, so dass sich diese Klasse modular überall benutzen lässt und im Hintergrund trotzdem Threads laufen. Wenn ich den Swingworker benutze und mich dann später doch für den stdout/console als "GUI" entscheide, so muss das Threadzeugs ja wieder neu implementiert werden.

Aber das sidn ja eh nur theoretische Überlegungen, man will es halt möglichst gut machen. Aber: wann tauscht man bei einem Hobbyprojekt in der Praxis schon das GUI...
Kurz gesagt kümmere ich mich mal etwas um den Swingworker, danke :)

Ganon
2010-04-19, 14:22:26
Achso ne, ich meinte das anders. Ich dachte anfangs es wäre cool, wenn die Threadarbeit von außerhalb der Logikklasse garnicht ersichtlich ist, so dass sich diese Klasse modular überall benutzen lässt und im Hintergrund trotzdem Threads laufen.

Das hat dann aber nichts mit deiner angesprochenen GUI zu tun. Denn irgendwo musst du warten bis deine Logik zuende ist, sonst kriegst du ja keine Info "fertig". Und rein in der Logik warten, würde das GUI blockieren.

Wenn ich den Swingworker benutze und mich dann später doch für den stdout/console als "GUI" entscheide, so muss das Threadzeugs ja wieder neu implementiert werden.
Aber das sidn ja eh nur theoretische Überlegungen, man will es halt möglichst gut machen. Aber: wann tauscht man bei einem Hobbyprojekt in der Praxis schon das GUI...
Kurz gesagt kümmere ich mich mal etwas um den Swingworker, danke :)

Naja, SwingWorker ist jetzt nicht so Mega-Komplex, das schreibt man auch ziemlich leicht selbst, ohne hier irgendwas aus dem Swing-Package zu nehmen (was aber eh Bestandteil von Java ist ;)).

Achja und mit der Einstellung "alles möglichst gut/flexibel/bla", haben sich schon die Java/Swing-Erfinder in die Füße geschossen in Sachen Threading. :D