Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
wie ich in einigen Beiträgen schon gesehen habe, sollten Threads mittels .invokeLater() benutzt werden.
Warum genau damit? Laut JavaAPI geht es doch einfach so:
Code:
PrimeRun p = new PrimeRun(143);
new Thread(p).start();
Warum sollte ich statt also anstatt den Thread "von Hand" zu starten ihn in ein neues Runnable-Objekt packen und ihn dann von invokeLater() starten zu lassen?
da gehts beileibe nicht um das Starten willkürlicher Threads,
sondern darum, eine bestimmte GUI-Aktion vom für die GUI zuständigen Thread durchführen zu lassen,
man kann in Java schlecht sagen 'hey du anderer Thread, führen diesen Code hier aus',
die Übergabe eines Runnable-Objektes (kein Thread!) ist eine mögliche Syntax
---------
das Starten eines Threads mit
new Thread(p).start();
selber in ein Runnable zu stecken und per invokeLater zu starten macht allerdings keinen Sinn (aus meiner Sicht)
in den invokeLater-run-Block gehören Befehle wie jscrollbar.setValue() oder so
Alles was im Event Dispatcher Thread ausgeführt werden muss (das sind alle Aktionen die direkt, oder indirekt die GUI beeinflussen und dabei Methoden aufrufen die nicht explizit als Threadsafe markiert sind), ruft man über invokeLater order invokeAndWait auf. Mit invokeAndWait sollte man allerdings sparsam sein, da es Deadlock Potential birgt.
> deine erste antwort schlägt sowas in der main() Methode vor.
da wird ein GUI-Objekt erzeugt, das ist der Lehre nach wahrscheinlich richtig in invokeLater, obwohl es meist auch ohne funktioniert,
der Thread hängt dann vom erzeugten Objekt ab, kann dann gezwungenermaßen kaum woanders erzeugt werden
import javax.swing.JProgressBar;
public class Progressbar implements Runnable
{
private static final long serialVersionUID = 1L;
private JProgressBar pBar;
public Progressbar()
{
pBar = new JProgressBar();
pBar.setMaximum(1700);
pBar.setBounds(20, 20, 260, 20);
}
public JProgressBar getProgressBar()
{
return pBar;
}
public void run()
{
for(int i = 0; i < pBar.getMaximum(); i++)
{
pBar.setValue(i);
pBar.repaint();
try
{
Thread.sleep(1);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
}
HauptGui-Klasse
Code:
public class Application extends JFrame
{
private static final long serialVersionUID = 1L;
public Application()
{
getContentPane().setLayout(null);
setSize(300, 80);
setTitle("Progress");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
Progressbar p = new Progressbar();
getContentPane().add(p.getProgressBar());
setVisible(true);
//new Thread(p).start();
SwingUtilities.invokeLater(p);
System.out.println("guithread");
System.out.println("guithread");
}
}
gehe ich Recht in der Annahme, dass ich mit "SwingUtilities.invokeLater(p);" der HauptGui mitteile: hör mit dem darstellen der Gui auf und führe diesen Code von p aus(updaten der JProgressBar)?
So scheint es sich jedenfalls zu Verhalten. Denn die beiden "System.out"s (die nichts mit der Gui zu tun haben) werden nebenläufig ausgeführt...
Und da die JProgressBar zur Gui gehört, die nicht mehr dargestellt wird, da sich der Thread um das updaten der JProgressBar values kümmert, wird sie auch erst wieder angezeigt nachdem das Updaten der values erledigt ist?
Dann verstehe ich aber nicht, warum man das "SwingUtilities.invokeLater()" aus dem Thread der für die Gui zuständig ist aus aufrufen soll, wenn dieser dadurch (was die Gui angeht) blockiert wird.
Die auskommentierte Zeile: "new Thread(p).start();" anstelle des "SwingUtilities.invokeLater()" führt zu einem gewollten Ergebnis. Die JProgressBar wird aktualisiert und ist dabei sichtbar.
Gibt es irgendeinen Grund, warum man das nicht so machen sollte?
nein, dafür gibt es keinen Grund,
längere Aufgaben gehören immer in einen eigenen Thread, nie in invokeLater(),
gut erkennbar an Thread.sleep() oder sonstiger synchronisation/ Warten,
den GUI-Thread würde niemand warten lassen
allein die GUI-Befehle wie
pBar.setValue(i);
pBar.repaint();
könnte man nun wieder per invokeLater sicher ausführen lassen,
aber nicht die ganze Schleife,
fürs repaint() wäre das auch nicht nötig, das sorgt sowieo intern für einen invokeLater oder ähnlichen Aufruf,
denkbar wäre außerdem, den Konstruktor Application() per invokeLater auszuführen, da der ne Menge GUI-Code enthält
Du schiebst deinen Worker Thread in den EDT und genau das sollst du nicht tun.
Die Rückmeldung aus dem Worker Thread geschiet über invokeLater, nicht andersrum.
Thread.sleep(1) ist übrigens viel zu kurz und dank einem Windows Bug kann dir das sogar die Systemzeit verstellen (wenn du ein Windows System verwendest).
langsam denke ich, ich sollte die 5 sek Stllstand meiner Gui in Kauf nehmen. Davon bekommt man ja Kopfschmerzen. Ich wusste schon warum ich mir solange Zeit gelassen habe mit Threads...
@Wildcard
Du schreibst, genau das sollte man nicht tun. Workerthread in EDT. Hast du ein Beispiel(am besten anhand meines Codes) wie es richtig herum aussehen muss? Ich blick im Moment voll nicht mehr durch :?
jetzt hab ich ja ein paar ansätze wie man sowas lösen kann.
@Wildcard
nachdem ich den Quellcode aus deinem Link etwas durchgegangen bin ist mir so einiges klarer geworden. Und das ist doch schonmal was :wink: . Warum er da in der setValue() Methode die NPE abfängt hab' ich zwar nicht verstanden, aber ich denke ich bin für heute erstmal bedient...
Tortzdem stelle ich fest:
Ich würde das "status" Objekt gar nicht erst null'en nachdem die Schleife durch ist(brauch ich also keine NPE abfangen). Weiss einer warum das aber angebracht sein könnte?
/*
* Der Algorithmus sollte nicht unnötig angehalten werden, nur damit die
* Progressbar schön aussieht, deshalb "invokeLater". Es kann nun aber
* passieren, dass der Algorithmus abbricht, nachdem er "setValue"
* aufgerufen hat (und damit die Variable "status" auf null setzt), das
* übergebene Runnable aber erst später ausgeführt wird. Aus diesem
* Grund wird ein try-catch-Block benutzt, der die mögliche
* NullPointerException abfängt. Alternativ könnte man mit dem Keyword
* "synchronized" einen Mechanismus bauen, der verhindert dass die
* Variable "status" verändert wird, während wir uns in dieser Methode
* befinden (und dann eine einfache if-Abfrage machen).
*/
SwingUtilities.invokeLater( new Runnable(){
public void run() {
try{
status.setValue( value );
}
catch( NullPointerException ex ){
// silent exception
}
}
});
edit: ok, ich muss eher deine Frage richtig lesen,
den Kommentart hast du wohl gelesen..
wegen GarbageCollection, einen großen Sinn sehe ich da aber auch nicht
@SlaterB
ja stimmt. den kommentar hatte ich gelesen . Bin nur nicht ganz schlau draus geworden... Egal.
@all
Gestern hab ich noch einmal meinen Programmcode geändert. Gemäss den neuen Erkenntnissen :wink:
Ich denke ich hab es jetzt so einigermassen kapiert. Also wenn einer von euch die (einigermassen) korrekte Anwendung von Threads und ProgressBar nocheinmal bestätigen könnte, würde ich diesen Beitrag als gelöst markieren.
hier mein Code:
Gui-Klasse
Code:
import javax.swing.JFrame;
import javax.swing.JProgressBar;
public class Application extends JFrame
{
private static final long serialVersionUID = 1L;
private JProgressBar jProgressBar;
public Application()
{
getContentPane().setLayout(null);
setSize(300, 80);
setTitle("Progress");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
jProgressBar = new JProgressBar();
jProgressBar.setMaximum(100);
jProgressBar.setBounds(20, 20, 260, 20);
getContentPane().add(jProgressBar);
Updater updater = new Updater(jProgressBar);
setVisible(true);
updater.startUpdating();
}
}
Updater-Klasse
Code:
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
public class Updater implements Runnable
{
private JProgressBar jpb;
public Updater(JProgressBar progress)
{
jpb = progress;
}
public void startUpdating()
{
new Thread(this).start();
}
public void run()
{
for(int i = 0; i < jpb.getMaximum(); i++)
{
setValue(i);
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
private void setValue(final int value)
{
SwingUtilities.invokeLater(new Runnable(){
public void run()
{
jpb.setValue(value);
}});
}
}