# Gui und Aufgaben Threads



## Sunchezz (9. Dez 2010)

Hallo Leuts, 

in meiner kleinen Software soll es eine kleine JProgressBar geben und ich habe mir gedacht, da ich das noch nie so wirklich zum laufen bekommen habe, aber schon viel drüber gelesen, Konstruiere ich meine Software von anfang an so das sie über zwei Threads läuft.
Habe hier im Forum gelesen das das generell sowieso ganz sinnvoll sein soll die GUI und die logischen aufgaben in zwei verschiedenen Threads zu trennen.
Nun für mich die Preisfrage, wie geht das?

Ich habe es bisher so versucht:
Ich habe eine Main-Klasse, in der werden zwei Objekte erstellt, einmal das GUI-Frame, und dann meine Controller-Klasse, in der wiederum alle anderen Teilaufgaben und klassen initialisiert werden.

MAIN-Klasse:

```
...
SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          programm = new Programm("Test");
          programm.setVisible(false);
          System.out.println(Thread.currentThread().toString());
        }
      });

      SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          controller.init();
          programm.setVisible(true);
          System.out.println(Thread.currentThread().toString());
        }
      });
...
```

allerdings tut sich hier herzlich wenig, und die ProgressBar verrichtet ihre arbeit auch nur auf die gute alte Art: Gui einfrieren und dann auf volle 100%


```
private Thread guiThread = new Thread() {
    public void run() {
      programm = new Programm("Test");
    }
  }
  private Thread logicThread = new Thread() {
    public void run() {
      controller = new MainController();
    }
  }
```
Auch dieser Versuch führte nicht zum gewünschten erfolg!

Wäre euch dankbar für jede Hilfe!

Sunny


----------



## Michael... (9. Dez 2010)

Sunchezz hat gesagt.:


> Habe hier im Forum gelesen das das generell sowieso ganz sinnvoll sein soll die GUI und die logischen aufgaben in zwei verschiedenen Threads zu trennen.


Es ist sinnvoll langwierige Aufgaben bzw. blockierende Aufgaben in einem separaten Thread abzuarbeiten.
Versuchst Du generell View und Controller in unterschiedlichen Threads zu initialisieren? Das macht nicht unbedingt Sinn.

Zu Code 1 Runnables mit SwingUtilities.invoke... aufgerufen laufen im EDT also laufen die vermeitlichen zwei Abläufe im selben Thread
Zu Code 1 unabhängig davon, ob das Sinn macht (wie) werden die Thread den gestartet?

Hier noch ein Beispiel zum Thema Thread:

```
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class SimpleThreadDemo extends  JFrame {
	private JLabel label;
	
	public SimpleThreadDemo () {
		label = new JLabel("0", JLabel.CENTER);
		this.getContentPane().add(label);
		new Thread(new MySpecialTask()).start();
	}
	
	
	class MySpecialTask implements Runnable {
		private int i;
		public void run() {
			i=0;
			// folgender Prozess läuft unendlich lange 
			// und aktualiert dabei ca. jede Sekunde die GUI 
			while (true) {   
				try {
					Thread.sleep(1000);				
					label.setText("" + ++i);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				JFrame frame = new SimpleThreadDemo();
				frame.setBounds(0, 0, 150, 100);
				frame.setLocationRelativeTo(null);
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				frame.setVisible(true);
			}
		});
	}
}
```


----------



## tfa (9. Dez 2010)

Der Swingworker würde sich grundsätzlich auch anbieten.
http://www.java-forum.org/blogs/tfa/25-snippet-swingworker.html


----------



## tagedieb (9. Dez 2010)

Ich empfehle dir mal diese Seite zu lesen. Ansonsten wirst du klaeglich scheitern.

GUIs are singlethreaded 

Das heisst, dass immer nur EIN Thread auf Swing Komponenten zugreifen duerfen (die ist der Event-Thread, welcher vom Swing Framework gestarted wird). Mit SwingUtilities.invokeLater(...) kannst du Aufgaben an den Event-Thread uebertragen. Dieser wird deine Tasks sequenziel abarbeiten. Also wenn du hier langwierige Aufgaben oder schlimmer noch endlos Schleifen uebergibst blockierst du das ganze GUI. 

Ja, ich aergere mich auch immer ueber schlecht implementiere Applikationen in welchen der 'Abbrechen ' Knopf nicht funktioniert und das ganze GUI einfriert. Das hat wohl schon jeder erlebt und das ist der Grund dafuer. ;-)


----------



## Sunchezz (9. Dez 2010)

@Michael...
Die Threads werden normal mit "start()" gestartet...

Und ja, ich dachte das wäre am sinnvollsten, wenn ich von vorne rein die "aufgaben-klassen" und das GUI in zwei verschiedene Threads packe, ich dachte dann würden die anderen initalisierten aufgaben-Objekte auch gleich in dem neuen Thread erzeugt?!

Oder muss ich jede einzelne logik-methode die nix mit der gui zu tun hat in nem neuen Thread packen?
und vor allem, manche von den logik sachen sollen dann ja im nachhinein noch das GUI updaten und mit neuen Daten versorgen. wie läuft das?

hab mir jetzt beide Seiten angeschaut, und größtenteils den Sinn verstanden, offenbar aber noch nicht ausreichend, denn ich weiß nich wie mir das weiterhilft!
Obwohl sich für mich der Swingworker am besten angehört hat xD


----------



## Sunchezz (9. Dez 2010)

So, ich kann ja jetzt einfach mal nen Konkretes Beispiel posten:

Das ist meine Methode:

```
public void checkeAlleProxys() {
    int listLenght = Start.controller.getSpeicher().getProxys().size();
    Start.programm.getProgressBar().setMinimum(0);
    Start.programm.getProgressBar().setMaximum(listLenght);
    for (int i = 0; i < listLenght; i++) {
      System.out.println("\nProxy " + i);
      if (this.checkProxy(Start.controller.getSpeicher().getProxys().get(i)) == true) {
        Start.controller.getSpeicher().getProxys().get(i).setIstErreichbar(true);
        Start.controller.getSpeicher().getProxys().get(i).setNutzbar(true);
      } else {
        Start.controller.getSpeicher().getProxys().get(i).setIstErreichbar(false);
        Start.controller.getSpeicher().getProxys().get(i).setNutzbar(false);
      }
      javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          Start.controller.updateProgressBar(1);
        }
      });
    }
  }
```

Und dieses Teil hier hab ich irgendwo als Beispiel runtergeladen:


```
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ProgressBarExample extends JPanel {

  JProgressBar pbar;
  static final int MY_MINIMUM=0;
  static final int MY_MAXIMUM=300;

  public ProgressBarExample() {
     pbar = new JProgressBar();
     pbar.setMinimum(MY_MINIMUM);
     pbar.setMaximum(MY_MAXIMUM);
     add(pbar);
  }

  public void updateBar(int newValue) {
    pbar.setValue(newValue);
  }

  public static void main(String args[]) {
     try {
       javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
     }catch (Exception e) {

     }

     final ProgressBarExample it = new ProgressBarExample();

     JFrame frame = new JFrame("Progress Bar Example");
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     frame.setContentPane(it);
     frame.pack();      // angucken
     frame.setVisible(true);

     for (int i = MY_MINIMUM; i <= MY_MAXIMUM; i++) {
       final int percent=i;
       try {
         SwingUtilities.invokeLater(new Runnable() {
             public void run() {
               it.updateBar(percent);
             }
         });
         java.lang.Thread.sleep(10);
       } catch (InterruptedException e) {;}
     }
  }
}
```
Das Funktioniert, wieso meins nicht?
meiner Meinung nach ist es genau das gleiche...
außer das in meiner Methode "Thread.sleep" durch die "Arbeit" ersetzt wurde die ja simuliert werden sollte.

also was nun?
Doch auf Threads umsteigen?

Swingworker hat im übrigen auch nicht so funktioniert wie ich wollte... 
Habs heute auch noch nicht so lange versucht, werd ich wohl noch mal machen müssen!

Ich steig einfach nicht dahinter wie das funktioniert!


----------



## tfa (9. Dez 2010)

> Das Funktioniert, wieso meins nicht?


Deine Aktion läuft im Event-Dispatch-Thread ab und blockiert ihn. Dadurch wird der Progressbar nicht neu gezeichnet.
Das Beispiel läuft im Hauptthread. Das ist der Unterschied.



> Swingworker hat im übrigen auch nicht so funktioniert wie ich wollte...
> Habs heute auch noch nicht so lange versucht, werd ich wohl noch mal machen müssen!


Das solltest du.


----------



## Sunchezz (9. Dez 2010)

Wo änder ich das denn dann??
bzw. wo steht das im Beispiel die Anweisung für den Hauptthread?
Oder anderesrum, wo steht bei meinem das es der EDT ist?


----------



## tfa (9. Dez 2010)

Die main-Methode startet im Main-Thread - ganz einfach. 
Du rufst deine Arbeitsmethode wahrscheinlich von einem ActionListener oder sowas auf, d.h. auf ein Event von der GUI hin. Und die werden nunmal im EDT erzeugt. Im EDT sollte man nur Dinge tun, die höchstens ein paar Millisekunden dauern können. Dann hat man mit einfrierenden GUIs keine Probleme.


----------



## Sunchezz (9. Dez 2010)

Alles klar... verstanden und abgespeichert =)

Und was kann ich nun genau tun?
was würdet ihr mir denn empfehlen?

Ich habe so einige sehr lang dauernde (teilweise 10 min) Methoden die über Buttons gestartet werden, wie kann ich die nun mit einer ProgressBar koppeln, und das einfrieren des GUI verhindern?
Wie gesagt, ich habe eine einzige Klasse für das GUI, und viele kleine andere die angesprochen werden sollen.

Was kann ich da nun machen?

Weiß das ich mich grad irgendwie bescheuert anstelle, aber das mit den ganzen Threads, Runnable und SwingWorker ergibt für mich keinen Sinn!
Verstehn tu ich den Mist ja, aber ich bekomme es einfach nicht hin das vernünftig in mein Programm zu übertragen.


----------



## Michael... (9. Dez 2010)

Innerhalb der actionPerformed startest Du einen neuen Thread in dem die lange dauernde Methode aufgerufen/abgearbeitet wird. Nach einzelnen Bearbeitungsschritten oder Schleifendurchgängen in dieser Methode wird der neue Status an die ProgressBar übermittelt.
Hinweis: Wenn man innerhalb nebenläufiger Threads auf GUI Komponenten zugreift, empfiehlt es sich (abhängig von den Methoden) dies wiederum innerhalb eines SwingUtilities.invokeLater... Konstrukts zu tun.


----------



## Sunchezz (10. Dez 2010)

danke für alle antworten... das letzte war gut und hat mich weiter gebracht...!

Ich finde zwar das das scheiße viel arbeit insgesamt ist aber nun gut xD


----------



## Sunchezz (10. Dez 2010)

An sich ist das ursprüngliche Problem geklärt, jetzt läuft in der Hinsicht alles wie es soll...!

Nur, wenn die Software beendet wird bevor der Thread abgearbeitet ist, erledigt er seine Aufgabe trotzdem noch bis zum Ende obwohl die Software mehr oder weniger "tot" ist.
Wie unterbinde ich das bitte?
Habe gelesen das, da ja alle gängigen Methoden deprecated sind, nur eine null-referenz einen Thread wirksam schliesst.
Daher dachte ich mir das ja theoretisch ein windowListener mit windowClosed() und der null-referenz eigentlich reichen sollte, tuts aber nicht.

Ich könnte ja einfach das Problem umgehen indem ich das Beenden der Software vor fertigstellung des Threads unterbinde, aber das fühlt sich für mich wie schummeln an ^^

wie händelt ihr das normaler weise?


----------

