# SwingWorker setProgress Problem



## gerdgerdgerd (17. Dez 2009)

Hallo zusammen,

ich habe einen Methode als SwingWorker implementiert, da ansonsten das Hauptprogramm warten würde. Um den Benutzer ein Feedback zu geben möchte ich den ProgressMonitor verwenden. Soweit ist auch alles klar. Nur wird bei mir der registrierte PropertyChangeListener nicht aufgerufen, wenn ich den Progress mit setProgress neu setze. 



> setProgress
> protected final void setProgress(int progress)
> 
> Sets the progress bound property. The value should be from 0 to 100.
> ...



Hier ist nun mein Quellcode:


```
public class TestSwingWorker extends SwingWorker<Void, Void> {

	private Cube cube;
	private List<Element> allElements;

	public TestSwingWorker(List<Element> elements) {
		allElements = elements;
	}

	@Override
	protected Void doInBackground() throws Exception {
		System.out.println("ENTER doInBackground()");

		setProgress(0);

		final int totalElementCount = allElements.size();
		int currentElementCount = 0;

		for (Element currentElement : allElements) {

			// do something
			if (++currentElementCount % 50 == 0 || currentElementCount >= totalElementCount) {
				int currentProgress = (int) ((double)currentElementCount / (double)totalElementCount * 100);
				System.out.println("setProgress " + currentProgress);
				setProgress(currentProgress);
			}
		}

		System.out.println("return null");
		return null;
	}

	@Override
	protected void done() {
		System.out.println("ENTER done()");

		// do something
	}
}


TestSwingWorker swingWorker = new TestSwingWorker(allElements);
swingWorker.addPropertyChangeListener(new PropertyChangeListener() {
	@Override
	public void propertyChange(PropertyChangeEvent event) {
		System.out.println("ENTER propertyChange()");
	}
});
swingWorker.execute();
```

Ich habe extra schon mit % 50 nur jede 50 Wertänderung mit setProgress gesetzt, trotzdem wird der PropertyChangeListener nicht aufgerufen. Hier die dazugehörige Console Ausgabe:



> ENTER doInBackground()
> ENTER propertyChange()
> setProgress 6
> setProgress 12
> ...



Eigentlich sollte er danach die done() Methode aufrufen. Also irgendwie scheint da etwas nicht so zu funktionieren, wie ich das gerne möchte  

Danke


----------



## Gast2 (17. Dez 2009)

Du solltest erstmal setProgress nicht im backround thread aufrufen weil du bist im falschen Thread um die UI zu aktualisieren...


----------



## gerdgerdgerd (17. Dez 2009)

Aber ist der SwingWorker nicht ein eigener Thread und den ProgressMonitor kann ich über den PropertyChangeListener aktualisieren. Wo soll ich setProgress ansonsten aufrufen?
Bei diesem Beispiel: 
Making Progress With Swing's Progress Monitoring API : Core Java Technologies Tech Tips (ziemlich in der Mitte), wird setProgress ähnlich verwendet.


----------



## SlaterB (17. Dez 2009)

```
public class Test
{
    public static void main(String[] args)
    {
        TestSwingWorker swingWorker = new TestSwingWorker();
        swingWorker.addPropertyChangeListener(new PropertyChangeListener()
            {
                @Override
                public void propertyChange(PropertyChangeEvent event)
                {
                    System.out.println("ENTER propertyChange(): " + event.getPropertyName() + ", " + 
                       event.getNewValue());
                }
            });
        swingWorker.execute();
    }
}

class TestSwingWorker
    extends SwingWorker<Void, Void>
{


    @Override
    protected Void doInBackground()
        throws Exception
    {
        System.out.println("ENTER doInBackground()");

        for (int i = 0; i < 10; i++)
        {
            System.out.println("setProgress: " + (i * 10));
            setProgress(i * 10);
            Thread.sleep(400);
        }
        setProgress(100);

        System.out.println("return null");
        return null;
    }

    @Override
    protected void done()
    {
        System.out.println("ENTER done()");

        // do something
    }
}
```
Ausgabe:
ENTER doInBackground()
setProgress: 0
ENTER propertyChange(): state, STARTED
setProgress: 10
ENTER propertyChange(): progress, 10
setProgress: 20
ENTER propertyChange(): progress, 20
setProgress: 30
ENTER propertyChange(): progress, 30
setProgress: 40
ENTER propertyChange(): progress, 40
setProgress: 50
ENTER propertyChange(): progress, 50
setProgress: 60
ENTER propertyChange(): progress, 60
setProgress: 70
ENTER propertyChange(): progress, 70
setProgress: 80
ENTER propertyChange(): progress, 80
setProgress: 90
ENTER propertyChange(): progress, 90
return null
ENTER propertyChange(): progress, 100
ENTER done()
ENTER propertyChange(): state, DONE


----------



## gerdgerdgerd (17. Dez 2009)

ich habe gerade herausgefunden, das dies ein Problem mit dem quellcode ist den ich ausführe, denn mit Thread.sleep(400) geht es bei mir auch. Das ist mein Quellcode den ich ausführe:


```
Cube cube = null;
		for (GridElement currentElement : gridElements) {

			if (currentElement instanceof Grid2DElement) {
				Grid2DElement elem = (Grid2DElement) currentElement;

				Vertex coords = elem.getCoordinates();
				if (cube == null) {
					cube = new Cube();
					cube.setName(name);
					cube.setHeight(height);
					cube.setWidth(elem.getWidth());
					cube.setDepth(elem.getHeight());
					cube.setAmbient(1f, 1f, 1f);
					cube.setX(coords.x);
					cube.setZ(coords.z);
					cube.setY(0);
					cube.setAngle(0, 0, 0);
				} else {
					SubCube currentSubCube = new SubCube();
					currentSubCube.setParent(cube);
					currentSubCube.setHeight(height);
					currentSubCube.setWidth(elem.getWidth());
					currentSubCube.setDepth(elem.getHeight());
					currentSubCube.setAmbient(1f, 1f, 1f);
					currentSubCube.setX(coords.x);
					currentSubCube.setZ(coords.z);
					currentSubCube.setY(0);
					currentSubCube.setAngle(0, 0, 0);

					cube.addSubcube(currentSubCube);
				}
			}

			if (++currentElementCount % 50 == 0 || currentElementCount >= totalElementCount) {
				Thread.sleep(100);
				int currentProgress = (int) ((double) currentElementCount / (double) totalElementCount * 100);
				System.out.println("setProgress " + currentProgress);
				setProgress(currentProgress);
			}
		}
```

sobald ich folgendes auskommentiere, funktioniert es wunderbar und der PropertyChangeListener wird aufgerufen :

```
if (cube == null) {
					cube = new Cube();
					cube.setName(name);
					....
				} else {
					SubCube currentSubCube = new SubCube();
					currentSubCube.setParent(cube);
					currentSubCube.setHeight(height);
					....

					cube.addSubcube(currentSubCube);
				}
```


----------



## SlaterB (17. Dez 2009)

besteht eine Frage?
alles was weniger als ein Wimpernschlag dauert, kann zu langsam sein, als dass Swing immer sofort reagiert


----------



## gerdgerdgerd (17. Dez 2009)

siehe den beitrag vorher, ich habe mein beitrag editiert. Jedenfalls muss an dem Quellcode liegen, warum auch immer.


----------



## Sonecc (17. Dez 2009)

Ist der Kram threadsicher?
Mal versucht ein Throwable abzufangen? vl fliegt ne versteckte Exception oder so


----------



## gerdgerdgerd (17. Dez 2009)

Der Kram wird doch im gleichen Thread abgearbeitet. Die "kritische" Thread phase kommt ja erst wenn done() aufgerufen wird  .


----------



## Sonecc (17. Dez 2009)

Da ich cube der subcube nicht kenne und das Problem wie du sagst, beim erzeugen eines der Objekte auftritt, schließe ich einfach mal darauf, dass dort etwas nicht threadsicher ist, oder eben etwas geworfen wird

Edit: Eventuell addSubCube()... ?
Was für eine Datenstruktur liegt dahinter?


----------



## gerdgerdgerd (17. Dez 2009)

ich habe Throwable in einem try catch abgefangen, aber es wurde nichts geworfen. Leider hängt Cube und SubCube mit zuviel zusammen und ist sehr OpenGL spezifisch aufgebaut.
Er läuft ja im Prinzip auch sauber durch, was man anhand der ausgabe auch gut sieht:

setProgress 7
setProgress 15
setProgress 23

nur will er nicht den PropertyChangeListener ausführen.

edit:
public ArrayList<SubCube> subCubes = new ArrayList<SubCube>();

Aber das läuft doch alles im gleichen thread ab?!?! ist ja nur eine for- schleife über die elemente und daraus wird ein "haupt" cube erstellt und die subcubes danach hinzugefügt.

Ich kommentiere nun einzelne zeilen aus, um das problem genauer zu lokalisieren.


----------



## Sonecc (17. Dez 2009)

sauber durchlaufen tut es keinesfalls, sonst hättest du keine probleme 
Wie gesagt, was mir so einfällt, ist dass die Datenstruktur eventuell nicht Threadsicher ist, die durch addSubCube() angesprochen wird.
Ansonsten, weiß ich leider auch nicht woran es liegen könnte


----------



## gerdgerdgerd (17. Dez 2009)

Also ich habe jetzt das Problem genauer lokalisiert. so funktioniert es wunderbar:

```
if (cube == null) {
	cube = new Cube();
	cube.setName(name);
else {
	SubCube currentSubCube = new SubCube();
	currentSubCube.setParent(cube);

	cube.addSubcube(currentSubCube);
}
```

sobald aber 

```
cube.setHeight(..
```
 aufgerufen wird, erscheint das Problem.


----------



## Sonecc (17. Dez 2009)

das ist doch schonmal ein ansatz, nun musst du "nur" noch schauen, was dort passiert, was nicht korrekt sein könnte


----------



## gerdgerdgerd (17. Dez 2009)

> Because the GLU implementation is not thread-safe



und genau hier hängt er:


```
GLContext context = canvas.getContext();
context.makeCurrent();
```

mist, das Problem kann ich dann leider nicht umgehen, da ich ansonsten zuviel verändern müsste und das zum größten teil innerhalb von fremden quellcode.


----------



## Sonecc (17. Dez 2009)

lag ich also richtig damit, dass irgendwas nicht threadsicher ist....
Eine Threadsichere alternative gibt es nicht?


----------



## gerdgerdgerd (17. Dez 2009)

die alternative wäre in dem fall, alles umschreiben ...


----------



## Ebenius (17. Dez 2009)

Dann mach doch folgendes: Debugger (zum Beispiel im Eclipse) anschmeißen, in die Situation reinlaufen lassen, VM anhalten, Monitore angucken... Dann findest Du raus, ob zwei (oder gar mehr) Threads sich zu Tode locken...

Ebenius


----------

