PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Java: GUI + 2 Threads



itsme
20-04-2004, 18:33
Hallo mal wieder...

Ich habe 2 Threads. Im ersten Thread läuft die Applikation mit GUI. Im 2. Thread werden prozessorintensive Funktionen gestartet. Wie kann ich jetzt verhindern dass das GUI völlig blockiert wird? Ich habe mal folgendes versucht:



Thread thrd = new Thread(this);
thrd.start();

while (thrd.isAlive())
{
try
{
Thread.sleep(100);
this.paintComponents(this.getGraphics());
}
catch (InterruptedException ie){}
}


Die funktioniert, kann aber nicht die beste Lösung sein, oder?

bischi
20-04-2004, 20:03
Also wenn ich richtig verstanden habe:

Thread 1: GUI
Thread 2: Berechnungen

Die GUI sollte jetzt eigentlich überhaupt nicht mehr blockiert sein, selbst wenn Thread2 zugespammt ist. Was du machst, ist, dass du nicht im neuen Tread wartest, sondern im Hauptthread (der mit der GUI). Mit Thread.sleep(100) und den anschliessenden Befehlen wartest du im Hauptthread 100ms um dann etwas neu zu zeichnen. Den neuen Thread beachtest du mit deinem Code gar nicht mehr, nachdem du ihn mal erzeugt hast.

Zum Thema Threads kann ich auf jeden Fall www.javabuch.de empfehlen; Einfach runterladen (gratis) und in Kapitel22 nachsehen, wies geht (musst wohl oder übel eine neue Klasse machen afaics).

MfG Bischi

itsme
20-04-2004, 20:34
Yep, funktionieren tut es ja, nur wird durch die ewige Abfragerei im ersten Thread etwas Leistung verschenkt. Das GUI muss nicht wirklich benutzbar sein, es soll nur nicht so aussehen wie wenn die Applikation eingefroren wäre.

peschmae
20-04-2004, 20:44
@itsme: Das was du da macht ist irgendwie zwischen zwei Welten.

Grundsätzlich hat man zwei weit verbreitete Möglichkeiten das responsivitätsproblem in Apps bei langen Operationen zu umgehen:

1) Man sorgt während den Operationen dafür (alles in einem Thread) dass regelmässig der Eventloop abgearbeitet wird - u.A. kommt damit auch das Neuzeichnen der Fenster

2) Man macht Threads - d.h. der eine Thread läuft im Hintergrund und die kommunizieren dann über irgendwelche Signale (z.B. synchronized Variablen oder was auch immer)

Was du hier machst ist du machst einen Thread der im Hintergrund läuft 2) aber dann lässt du den ersten Thread nicht einfach sein sondern "sperrst" ihn wie aus 1) in eine lange Operation ein (hier ne Schleife mit sleep und neuzeichnen) und lässt ihn hauptsächlich warten und n bisschen Zeichnen.

Wie du das mit Swing-Guis am elegantesten löst weiss ich nicht. Hab sowas nie gemacht. Ein Problem könnte hier werden dass Swing den Eventloop normalerweise vor dir "versteckt" - das Zeugs ist im geheimen schon Multithreaded.

Die "richtige Richtung" wäre dass du z.B. beim Klick auf Start den Hintegrundrechnungsthread startest und den laufen lässt. Also:


Thread thrd = new Thread(this);
thrd.start();

der Thread "weiss" selber am besten wann er wie weit ist (Statusmeldung?) oder wann er fertig ist und löst dann die entsprechenden Ereignisse aus.

MfG Peschmä

bischi
20-04-2004, 20:59
Yep, funktionieren tut es ja, nur wird durch die ewige Abfragerei im ersten Thread etwas Leistung verschenkt. Das GUI muss nicht wirklich benutzbar sein, es soll nur nicht so aussehen wie wenn die Applikation eingefroren wäre.

Machst du aber - hab ich dir versucht, zu erklären: Ich nehme mal an, du hast eine Klasse (nennen wir die Test), die von einer anderen Klasse (AWT oder Swing) ein Fenster erbt. Du wirst wohl im Konstruktor (Test() {...}) deine GUI zeichnen und dann deine Schleife aufrufen. Damit verhinderst du aber, dass der ganze Konstruktor abgearbeitet werden kann und dein Programm auf Nachrichten reagieren kann - das Programm (also die GUI) "friert" ein. Egal was du machst - die GUI kann nicht auf Nachrichten (Eingaben, Klicks,...) reagieren.

Wie du es richtig machen musst (neue Klasse erstellen, die von Thread erbt und die berechnungen durchführt) und wie du alles miteinander verknüpfen musst, steht ausführlich (und relativ lang) im www.javabuch.de im Kapitel 22.

MfG Bischi

peschmae
20-04-2004, 21:07
Aus dem Thread raus Gui-Sachen manipulieren geht bei Swing übrigens ganz ähnlich wie bei SWT - habs eben mal nachgeguckt.

Du machst ne Klasse die "Runnable" implementiert und den Code enthält, den du aus dem nicht-Gui Thread heraus ausführen willst (und der die Gui manipuliert)


public class Updater implements Runnable {
String wichtigesArgument;
public Updater(String wichtigesArgument) {
this.wichtigesArgument = wichtigesArgument;
}

public void run() {
label.setText(wichtigesArgument);
}
}


dann tust du im Rechnungsthread sowas ausführen:


Runnable updater = new Updater("mein extrem wichtiger neuer TExt für das Olle label");
EventQueue.invokeLater(updater);


alternativ gibts auch EventQueue.invokeAndWait() - das wartet im Hintergrundthread dann so lange bis der Gui-Thread das Zeugs ausgeführt hat.

MfG Peschmä

bischi
20-04-2004, 21:28
Oder so was (Beispiel aus www.javabuch.de übernommen):


001 /* Listing2201.java */
002
003 class MyThread2201
004 extends Thread
005 {
006 public void run()
007 {
008 int i = 0;
009 while (true) {
010 System.out.println(i++);
011 }
012 }
013 }
014
015 public class Listing2201
016 {
017 public static void main(String[] args)
018 {
019 MyThread2201 t = new MyThread2201();
020 t.start();
021 }
022 }

MfG Bischi

fonz
21-04-2004, 07:47
so wie es peschmae beschrieben
hat, (mit dem interface Runnable) muss man es
unbedingt machen bei swing, da dort nicht alle
klassen threadsicher sind....


dort ist's ziemlich gut erklaert:
http://www.javaworld.com/javaworld/jw-06-2003/jw-0606-swingworker.html

ausserdem google mal nach SwingWorker - diese klasse erleichtert
so einiges....

bischi
21-04-2004, 12:14
Spielt afaics absolut keine Rolle, wenn du synchronized-Variabeln zum Daten übermitteln brauchst.

MfG Bischi

fonz
21-04-2004, 13:03
@bischi:

genau, dass ist ja das problem, swing benutzt intern eben
keine solchen 'synchronized variablen'....

solange du in einem 2. thread nur berechnest ist alles gut...
wenn du aber eine gui hast, willst du frueher oder spaeter auch
irgendein ergebnis 'sehen'

wie zb. im obigen beispiel setText("bla bla")....

mach das mal mit 2 oder mehr threads aufs selbe objekt
und es wird knallen..... ;-)

bischi
21-04-2004, 14:57
Hab selber gerade eben was gebastelt und mein Problem glaub ich gefunden:

an run() kann man keine Argumente übergeben - sonst wäre das ganze überhaupt kein Problem.

MfG Bischi

peschmae
21-04-2004, 14:59
Original geschrieben von bischi
Spielt afaics absolut keine Rolle, wenn du synchronized-Variabeln zum Daten übermitteln brauchst.


Daten übermitteln ja. Aber wenn du z.B. was an der GUI ändern willst (e.g. ProgressBar, Ergebnisanzeige, was weiss ich) dann geht das mit synchronized Variablen nicht mehr wirklich praktisch.

MfG Peschmä

bischi
22-04-2004, 12:49
Hab das Problem gesehen - Peschmäs Lösung scheint die am wenigsten komplizierte zu sein.

MfG Bischi