# Gui blockiert / friert ein



## Jens81 (11. Jan 2010)

Hallo zusammen,

ich lade größere Datenmengen aus einer Datenbank. Diese Daten bzw. diverse Statistiken werden in der Anwendung angezeigt.

Nun soll der Benutzer die Möglichkeit haben, die geladenen Daten zu aktualisieren. Jetzt das Problem: Realisiere ich das Neu-laden der Daten im Thread, friert die GUI manchmal bzw. öfters ein. Es wird keine Exception geworfen. Hat der GC vielleicht etwas damit zu tun? Oder woran könnte es noch liegen?

Implementiere ich die Funktion ohne Thread, läuft sie immer durch.

Allerdings soll diese Funktion als Thread implementiert werden, damit die restliche GUI nicht blockiert wird.

Da keine Exception geworfen wird, bin ich ziemlich ratlos???:L

Gruß,
Jens


----------



## javimka (11. Jan 2010)

Startest du den Thread auch sicher mit start() und nicht mit run()?

Zeig mal etwas Code, sonst können wir nur raten.


----------



## Jens81 (11. Jan 2010)

Insgesamt sind es schon sehr viele Zeilen Code, aber der Aufruf läuft so (Thread auskommentiert):


```
private void ladeModellNeu() {/*
		Thread ladeModellNeuThread = new Thread() {
			@Override
			public void run() {*/
				fenster.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
				DisabledGlassPane glass = new DisabledGlassPane();
				JRootPane rootPane = SwingUtilities.getRootPane(fenster);
				rootPane.setGlassPane(glass);
				glass.activate("Bitte warten...");
				
				ladevorgang = true;
				modell.removeAllItems();
				
				ladeDaten(null);
				
				setContentReiter1();
				setContentReiter2();
				setContentReiter3();
				setContentReiter4();
				setContentReiter5();
				setContentReiter6(true);

				ladevorgang = false;

				glass.deactivate();
				fenster.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));/*
			}
		};
		ladeModellNeuThread.start();*/
	}
```

PS: Wie gesagt, so auskommentiert läuft alles durch, nur wenn ich den Thread reinnehme geht es öfters nicht...

Mein Verdacht war ursprünglich der Garbage Collector, da ich während des Ladevogangs schon viel Speicher benötige... allerdings kann ich mir den Unterschied zwischen den Versionen mit und ohne Thread nicht erklären


----------



## SlaterB (11. Jan 2010)

wenn sowieso ein GlassPane die GUI extra sperrt, wie kannst du dann feststellen, ob sie blockiert oder nicht? 

> Wie gesagt, so auskommentiert läuft alles durch, nur wenn ich den Thread reinnehme geht es öfters nicht... 

geht was genau nicht?


----------



## Michael... (11. Jan 2010)

Jens81 hat gesagt.:


> ```
> fenster.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
> DisabledGlassPane glass = new DisabledGlassPane();
> JRootPane rootPane = SwingUtilities.getRootPane(fenster);
> ...


Die Änderungen an der GUI müssen hier raus. In den Thread sollten nur die Änderungen der Daten vorgenommen werden.


----------



## Gast2 (11. Jan 2010)

Nimm ein SwingWorker...und GUI Ändeurngen sollten zum EDT synchronisert werden...


----------



## Jens81 (11. Jan 2010)

Die GlassPane dient nur dazu, den User vom Drücken irgendwelcher Knöpfe während eines Ladevorgangs abzuhalten.

Mit "die GUI friert ein" meine ich, dass nicht nur das akutelle, sondern alle Fenster nicht mehr bedienbar sind. Öffne ich beispielsweise via Firefox Google und minimiere den Browser, zeigt die Anwendung nicht mehr ihre Oberfläche, sondern den "Rest" vom Browser (bzw. jeder anderen Anwednung die "darüber" liegt)... Ich hoffe es ist klar geworden was ich meine (?)


----------



## javimka (11. Jan 2010)

Läuft die ganze Methode run() denn auch wirkich vollständig ab, bzw. kommt es jemals zu einem glass.deactivate()?


----------



## Jens81 (11. Jan 2010)

@Michael... Ich habe mehrere ähnliche Funktionen mit kleineren Berechnungen, in denen die Änderungen an der GUI (GlassPane) so ohne Probleme liefen. Aber ich werd's testen...

@javimka Manchmal ja, manchmal nein... wenn nicht, dann bleibt es ziemlich am Anfang in der setContentReiter1 stecken. Aber da wird nichts wildes gemacht und auch kein Fehler geworfen


----------



## javimka (11. Jan 2010)

Dann deutet es auf eine Endlos-Schleife hin. Wie sieht setContentReiter1() denn aus?


----------



## Jens81 (11. Jan 2010)

Da werden nur JTextFields gefüllt...

```
private void setContentReiter1() {
		/**
		 * Setzt den geladenen Inhalt für Reiter 1.
		 * - Allgemeine Modellinformationen
		 */
		art.setText(((ItemModell)modell.getSelectedItem()).getArt());
		produktgruppe.setText(((ItemModell)modell.getSelectedItem()).getProduktgruppe());
		ausgangsvariable.setText(((ItemModell)modell.getSelectedItem()).getAusgangsvariable());
		ausgangsvariable.setCaretPosition(0);
		zielvariable.setText(((ItemModell)modell.getSelectedItem()).getZielvariable());
		zielvariable.setCaretPosition(0);
		trainingsdaten.setText(((ItemModell)modell.getSelectedItem()).getTrainingsdaten());
		trainingsdaten.setCaretPosition(0);
...
}
```


----------



## Jens81 (11. Jan 2010)

Michael... hat gesagt.:


> Die Änderungen an der GUI müssen hier raus. In den Thread sollten nur die Änderungen der Daten vorgenommen werden.



Wieso sollte ich denn hier keine GUI Änderungen reinnehmen? Wie sollte das dann aussehen?
Ist das Setzen von Text in ein JTextField auch schon eine GUI-Änderung?

Ergänzung: Hab die GlassPane und den Mauszeigerwechsel mal deaktiviert und es scheint jetzt durchzulaufen, werd's aber mal in Ruhe testen. Wie gesagt, in anderen Funktionen bin ich ähnlich vorgegangen und da war es nie ein Problem ???:L


----------



## javimka (11. Jan 2010)

Ja, ist es. Modifikationen an der GUI sollten im Event-Dispatching-Thread (EDT) laufen. Um einen Befehl aus einem anderen Thread im EDT ausführen zu lassen, kannst du schreiben

```
SwingUtilities.invokeLater(new Runnable() {
  public void run()  {
    // Befehle
  }
});
```


----------



## Gast2 (11. Jan 2010)

Jens81 hat gesagt.:


> Ist das Setzen von Text in ein JTextField auch schon eine GUI-Änderung?



ja ist es, aber das hat immer geklappt weil soviel ich wieß setText() thread safe ist...
Das ist schade das Swing hier keine Exception schmeißt wenn man im falschen Thread ist...


----------



## javimka (11. Jan 2010)

Stimmt, setText ist Thread-safe, das wusste ich nicht. Es ist also eine GUI-Änderung, aber die kannst du in jedem Thread aufrufen.


----------



## Jens81 (12. Jan 2010)

Ich habe versucht, eure Korrekturen in der Methode umzusetzen und es scheint zu laufen.


```
private void ladeModellNeu() {
		Thread ladeModellNeuThread = new Thread() {
			@Override
			public void run() {
				final DisabledGlassPane glass = new DisabledGlassPane();
				SwingUtilities.invokeLater(new Runnable() {
					public void run()  {
						fenster.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
						JRootPane rootPane = SwingUtilities.getRootPane(fenster);
						rootPane.setGlassPane(glass);
						glass.activate("Bitte warten...");
					}
				});

				ladevorgang = true;
				modell.removeAllItems();
				
				ladeDaten(null);
				
				try {
					SwingUtilities.invokeAndWait(new Runnable() {
						public void run()  {
							setContentReiter1();
							setContentReiter2();
							setContentReiter3();
							setContentReiter4();
							setContentReiter5();
							setContentReiter6(true);
						}
					});
				}
				catch (Exception e) {
					e.printStackTrace();
				} 

				ladevorgang = false;
				
				SwingUtilities.invokeLater(new Runnable() {
					public void run()  {
						glass.deactivate();
						fenster.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
					}
				});
			}
		};
		ladeModellNeuThread.start();
	}
```


----------



## Gast2 (12. Jan 2010)

Wie gesagt ich würde  dafür einen SwingWorker nehmen sieht der code ein bischen Übersichtlicher aus =)...


----------



## Jens81 (12. Jan 2010)

Ich habe leider kaum Erfahrung in der GUI-Welt  Ich bin gerade dabei mich in den SwingWorker einzulesen.


----------



## Gast2 (12. Jan 2010)

Jens81 hat gesagt.:


> Ich habe leider kaum Erfahrung in der GUI-Welt  Ich bin gerade dabei mich in den SwingWorker einzulesen.


Man lernt nie aus ...
Versuchs einfach und bei Problemen einfach den Code posten dann wird es schon klappen...


----------



## Jens81 (12. Jan 2010)

Im bisherigen Programm ist es ja so, dass ich als erstes ein GlassPane über die Gui lege, dann die Daten lade, die Gui mit den neuen Daten aktualisiere und abschließend die GlassPane wieder deaktiviere.

Wie kann ich denn diesen Ablauf (speziell: GlassPane) in einem SwingWorker abbilden?

Erster Ansatz:


```
private void ladeModellNeu() {
		SwingWorker worker = new SwingWorker() {
		
			@Override
			public Object doInBackground() throws Exception {
				ladeDaten(null); //es werden größere Datenmengen geladen
				
				return null; //void geht leider nicht?
			}
			
			@Override
			protected void done(){
				setContentReiter1();
				setContentReiter2();
				setContentReiter3();
				setContentReiter4();
				setContentReiter5();
				setContentReiter6(true);
			}
			
		};
		worker.execute();
}
```


----------



## Gast2 (12. Jan 2010)

Jens81 hat gesagt.:


> Im bisherigen Programm ist es ja so, dass ich als erstes ein GlassPane über die Gui lege, dann die Daten lade, die Gui mit den neuen Daten aktualisiere und abschließend die GlassPane wieder deaktiviere.
> 
> Wie kann ich denn diesen Ablauf (speziell: GlassPane) in einem SwingWorker abbilden?
> 
> ...


Einfach bevor du ihn startest?!
Also vor den SwingWorker


----------



## Jens81 (12. Jan 2010)

Aber wenn ich die GlassPane vor und nach dem Worker ausführe, wird sie ja in gar keinem Thread mehr ausgeführt, weder in meinem noch im EDT. ???:L


----------



## Michael... (12. Jan 2010)

Jens81 hat gesagt.:


> Aber wenn ich die GlassPane vor und nach dem Worker ausführe, wird sie ja in gar keinem Thread mehr ausgeführt, weder in meinem noch im EDT. ???:L


Doch das aktivieren des GlassPane läuft im EDT.
Das deaktivieren kann man in die done() des SwingWorker stecken.


----------



## Gast2 (12. Jan 2010)

Jens81 hat gesagt.:


> Aber wenn ich die GlassPane vor und nach dem Worker ausführe, wird sie ja in gar keinem Thread mehr ausgeführt, weder in meinem noch im EDT. ???:L



Wie wird in gar keinem ausgeführt ^^??? Es wird auf jeden Fall im EDT ausgeführt und da gehört sie hin^^... und wie oben schon gesagt das deactivieren kommt in done

EDIT:


```
private void ladeModellNeu() {
      final DisabledGlassPane glass = new DisabledGlassPane();
       fenster.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        RootPane rootPane = SwingUtilities.getRootPane(fenster);
        rootPane.setGlassPane(glass);
        glass.activate("Bitte warten...");
        SwingWorker worker = new SwingWorker() {
        
            @Override
            public Object doInBackground() throws Exception {
                ladeDaten(null); //es werden größere Datenmengen geladen
                
                return null; //void geht leider nicht?
            }
            
            @Override
            protected void done(){
                setContentReiter1();
                setContentReiter2();
                setContentReiter3();
                setContentReiter4();
                setContentReiter5();
                setContentReiter6(true);
                glass.deactivate();
               //usw. UI SACHEN
            }
            
        };
        worker.execute();
}
```


----------



## Jens81 (12. Jan 2010)

```
private void ladeModellNeu() {
		final DisabledGlassPane glass = new DisabledGlassPane();
		fenster.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		JRootPane rootPane = SwingUtilities.getRootPane(fenster);
		rootPane.setGlassPane(glass);
		glass.activate("Bitte warten...");
		
		SwingWorker worker = new SwingWorker() {
		
			@Override
			public Object doInBackground() throws Exception {
				ladeDaten(null);
				
				return null;
			}
			
			@Override
			protected void done(){
				setContentReiter1();
				setContentReiter2();
				setContentReiter3();
				setContentReiter4();
				setContentReiter5();
				setContentReiter6(true);

				glass.deactivate();
				fenster.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
			}
			
		};
		worker.execute();
}
```

Edit: Jetzt gehts


----------



## Gast2 (12. Jan 2010)

noch ne frage?^^


----------



## Jens81 (12. Jan 2010)

Hab aber noch eine andere Frage 

Worin liegt denn der (ablauftechnische) Unterschied zwischen den beiden Verfahren? Lasse ich das Verfahren via SwingWorker laufen, gibt es im setContentReiter6 eine NullPointer Exception (habe noch nicht nach dem Grund gesucht). Dieser wird bei der zweiten Methode (SwingUtilities.invokeAndWait) nciht ausgelöst. Ändere ich die zweite Methode auf SwingUtilities.invokeLater, tritt diese Exception auch auf.


```
final DisabledGlassPane glass = new DisabledGlassPane();
		fenster.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		JRootPane rootPane = SwingUtilities.getRootPane(fenster);
		rootPane.setGlassPane(glass);
		glass.activate("Bitte warten...");
		
		SwingWorker worker = new SwingWorker() {
		
			@Override
			public Object doInBackground() throws Exception {
				ladeDaten(null);
				
				return null;
			}
			
			@Override
			protected void done(){
				setContentReiter1();
				setContentReiter2();
				setContentReiter3();
				setContentReiter4();
				setContentReiter5();
				setContentReiter6(true);
				
				glass.deactivate();
				fenster.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
			}
			
		};
		worker.execute();
```



```
Thread ladeModellNeuThread = new Thread() {
			@Override
			public void run() {
				final DisabledGlassPane glass = new DisabledGlassPane();
				try {
					SwingUtilities.invokeAndWait(new Runnable() {
						public void run()  {
							fenster.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
							JRootPane rootPane = SwingUtilities.getRootPane(fenster);
							rootPane.setGlassPane(glass);
							glass.activate("Bitte warten...");
						}
					});
				} catch (Exception e) {
					e.printStackTrace();
				}

				ladevorgang = true;
				modell.removeAllItems();
				
				ladeDaten(null);
				
				try {
					SwingUtilities.invokeAndWait(new Runnable() {
						public void run()  {
							setContentReiter1();
							setContentReiter2();
							setContentReiter3();
							setContentReiter4();
							setContentReiter5();
							setContentReiter6(true);
						}
					});
				} catch (Exception e) {
					e.printStackTrace();
				} 

				ladevorgang = false;
				
				try {
					SwingUtilities.invokeAndWait(new Runnable() {
						public void run()  {
							glass.deactivate();
							fenster.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
						}
					});
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		};
		ladeModellNeuThread.start();
```


----------



## Gast2 (12. Jan 2010)

Jens81 hat gesagt.:


> Hab aber noch eine andere Frage
> 
> Worin liegt denn der (ablauftechnische) Unterschied zwischen den beiden Verfahren? Lasse ich das Verfahren via SwingWorker laufen, gibt es im setContentReiter6 eine NullPointer Exception (habe noch nicht nach dem Grund gesucht). Dieser wird bei der zweiten Methode (SwingUtilities.invokeAndWait) nciht ausgelöst. Ändere ich die zweite Methode auf SwingUtilities.invokeLater, tritt diese Exception auch auf.
> 
> ...



Les doch mal die API...
nach dem invokeAndWait warter er... bis das Runnable feritg ist und geht dann erst im Code weiter...
bei invoke schmeißt er das Runnable in eine Queue und geht im Code sofort weiter und fürt die runnables in der Queue nacheinander aus...


----------



## Jens81 (12. Jan 2010)

Die API habe ich gelesen und der Unterschied zwischen invokeAndWait und invokeLater ist mir klar. Aber der ablauftechnische Unterschied zwischen den beiden geposteten Code-Schnippseln nicht. Der müsste doch gleich sein ???:L


----------



## Gast2 (12. Jan 2010)

Jens81 hat gesagt.:


> Die API habe ich gelesen und der Unterschied zwischen invokeAndWait und invokeLater ist mir klar. Aber der ablauftechnische Unterschied zwischen den beiden geposteten Code-Schnippseln nicht. Der müsste doch gleich sein ???:L



Nö ist err nicht da fehlt ein removeAllItems z.B. noch


----------



## Jens81 (12. Jan 2010)

Oh, dass muss beim C&P verloren gegangen sein. Dann scheint der Ablauf ja doch identisch zu sein. Vielen Dank!


----------

