Discussion:
verzoegert Kreise zeichnen ?
(zu alt für eine Antwort)
Ernst Baumann
2009-02-26 20:19:46 UTC
Permalink
Hallo allerseits,
1)
a)
In dem einfachen Demo-Programm unten werden verzögert Kreise auf dem
Bildschirm dargestellt. Das Programm funktioniert zwar problemlos,
aber ich verstehe nicht ganz genau, warum es problemlos funktioniert.

Mit invokeLater(...) werden jeweils die sich in run(..) befindlichen
Anweisungen in die Warteschlange des EDT gestellt.
Da der EDT ein Thread ist, gibt es innerhalb dieses Threads keine
parallelen, konkurrierenden Zugriffe auf die gleichen Variablen.
Deshalb gibt es keine Probleme, wenn es innerhalb dieses EDT
Lesezugriffe (wie z.B. beim Zeichnen eines Kreises auf die Koordinaten
) und Schreibzugriffe (wie z.B. die Koordinaten eines Kreises setzen)
auf die _gleiche_ Variable gibt.

b)
Neben diesem EDT gibt es den Main-Thread (in dem die Methode main
abläuft)
In diesem gibt es u.a. die folgenden Anweisungen:
stelle = 1;
Diagramm diagramm = new Diagramm(550, 550);
JFrame f = new JFrame();
f.setSize(550,550);
f.getContentPane().add(diagramm);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);

Das heißt im Main-Thread gibt es u.a. auf die folgenden Variablen
Schreibzugriffe:
stelle
diagramm
f

Aber im EDT wird auf die Variable
stelle
und indirekt auf diagramm (über das Attribut myimg in diagramm)
und indirekt auf f über f.getContentPane().add(diagramm);
zugegriffen (also auf die gleichen Variablen wie im Main-Thread).

Man hat also _konkurrierende_ Zugriffe in 2 verschiedenen Threads auf
die _gleichen_ Variablen.
Warum gibt es keine Probleme (die Zugriffe werden ja nicht durch
synchronized legal geregelt)?

c)
Meine Erklärung:
Vielleicht geschieht der Zugriff auf diese Variablen im EDT zeitlich
_nach_ dem Main-Thread:
c1)
Die Anweisung f.setVisible(true) im Main-Thread löst den Aufruf von
repaint() aus.
repaint stellt die entsprechenden Anweisungen in den EDT und fordert
_dann_ den EDT-Thread an

c2)
Die Anweisung invokeLater(...) stellt die Anweisungen in run(...) in
den EDT und fordert _dann_ den EDT-Thread an.
Aber wann der EDT ausgeführt wird, bestimmt doch das Betriebssystem.
Deswegen ist mir dies nicht richtig klar!

Sind meine Erklärungn richtig, bzw. was stimmt nicht und muss ergänzt
werden?

2)
a)
Mir wurde gesagt:
Von außerhalb des EDT soll nicht auf bereits realisierte
Swing/AWT-Objekte zugegriffen werden.
Das wird doch aber bei meinem Programm im Main Thread gemacht, z.B.
werden auf die folgenden Variablen (Swing/AWT-Objekte) zugegriffen:
diagramm
f

b)
Die Aussage bei 2a) müsste eigentlich noch verschärft werden:
Man darf eigentlich gar nicht von außerhalb, also von einem anderen
Thread auf den EDT zugreifen, außer man regelt das mit synchronized.
Ist das richtig?


mfg
Ernst


----------------------------------------------------------------

package verzoegertzeichnen3;

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

public class MainVerzoegertZeichnen3 {
public static void main(String[] args){
int stelle;
stelle = 1;
Diagramm diagramm = new Diagramm(550, 550);
JFrame f = new JFrame();
f.setSize(550,550);
f.getContentPane().add(diagramm);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);

while(stelle<=10){
System.out.println("stelle in main="+stelle);
try{
Thread.sleep(100);
}
catch(Exception e){}
stelle=stelle+1;

// ohne anonyme Klasse
MyRunnable myR = new MyRunnable(diagramm, stelle);
javax.swing.SwingUtilities.invokeLater(myR);


}
}
}


class Diagramm extends JPanel{
private int xpAnz;
private int ypAnz;
private int ort;
private Image myimg;
private Graphics myg;
private int sx;
private int sy;

public Diagramm(int xpAnz, int ypAnz){
ort=0;
this.xpAnz=xpAnz;
this.ypAnz=ypAnz;
myimg=null;
}

public void setOrt(int pOrt){
ort=pOrt;
}


// ist im EDT (siehe Methode void run)
public void male(){
if(myimg==null){
sx = this.getSize().width;
System.out.println("sx="+sx);
System.out.println("sy="+sy);

sy = this.getSize().height;
myimg = createImage(sx, sy);
myg = myimg.getGraphics();
}
// Schreibzugriff auf myimg
myg.setColor(Color.red);
myg.drawOval(ort*20 , ort*20, 20, 20);
}

// ist im EDT
public void paintComponent(Graphics g){
System.out.println("paintComponent");
// Lesezugriff auf myimg
g.drawImage(myimg,0,0,null);
}
}


class MyRunnable implements Runnable{
private Diagramm diagramm;
private int ort;
public void run(){
// ist im EDT
// Schreibzugriff auf ort
diagramm.setOrt(ort);
// Schreibzugriff auf mying
diagramm.male();
// Lesezugriff auf mying in paintComponent
diagramm.repaint();
}

public MyRunnable(Diagramm pDiagramm, int pOrt){
diagramm = pDiagramm;
ort = pOrt;
}
}

----------------------------------------------------------------
Jochen Theodorou
2009-02-26 20:47:57 UTC
Permalink
Post by Ernst Baumann
Hallo allerseits,
Hallo Ernst, schon lange keine dieser posts von dir mehr gehabt ;)
Post by Ernst Baumann
1)
a)
In dem einfachen Demo-Programm unten werden verzögert Kreise auf dem
Bildschirm dargestellt. Das Programm funktioniert zwar problemlos,
aber ich verstehe nicht ganz genau, warum es problemlos funktioniert.
Mit invokeLater(...) werden jeweils die sich in run(..) befindlichen
Anweisungen in die Warteschlange des EDT gestellt.
ja
Post by Ernst Baumann
Da der EDT ein Thread ist, gibt es innerhalb dieses Threads keine
parallelen, konkurrierenden Zugriffe auf die gleichen Variablen.
dazu müsste es ja einen anderen Thread geben, aber das hast du mit
"innerhalb dieses Threads" ausgeschlossen... also ja

[...]
Post by Ernst Baumann
b)
Neben diesem EDT gibt es den Main-Thread (in dem die Methode main
abläuft)
stelle = 1;
Diagramm diagramm = new Diagramm(550, 550);
JFrame f = new JFrame();
f.setSize(550,550);
f.getContentPane().add(diagramm);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
Das heißt im Main-Thread gibt es u.a. auf die folgenden Variablen
stelle
diagramm
f
Aber im EDT wird auf die Variable
stelle
und indirekt auf diagramm (über das Attribut myimg in diagramm)
und indirekt auf f über f.getContentPane().add(diagramm);
zugegriffen (also auf die gleichen Variablen wie im Main-Thread).
Man hat also _konkurrierende_ Zugriffe in 2 verschiedenen Threads auf
die _gleichen_ Variablen.
Warum gibt es keine Probleme (die Zugriffe werden ja nicht durch
synchronized legal geregelt)?
da sind ein paar Sachen falsch... stelle ist in
Post by Ernst Baumann
Post by Ernst Baumann
package verzoegertzeichnen3;
import java.awt.*;
import javax.swing.*;
public class MainVerzoegertZeichnen3 {
public static void main(String[] args){
int stelle;
stelle = 1;
Diagramm diagramm = new Diagramm(550, 550);
JFrame f = new JFrame();
f.setSize(550,550);
f.getContentPane().add(diagramm);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
while(stelle<=10){
System.out.println("stelle in main="+stelle);
try{
Thread.sleep(100);
}
catch(Exception e){}
stelle=stelle+1;
// ohne anonyme Klasse
MyRunnable myR = new MyRunnable(diagramm, stelle);
javax.swing.SwingUtilities.invokeLater(myR);
}
}
}
eine lokale Variable und du benutzt keine annonyme inner Klasse, also
kann es keinen anderen Thread geben, der auf die genau gleiche Variable
zugreift. Du übergibst zwar stelle an MyRunnable, aber das ist eine
Kopie des Wertes (int ist ein primitiver Datentyp), etwas konkurierendes
kann hier so nicht entstehen.

Diagramm ist nicht primitiv, also könnte es theoretisch Konflikte geben,
wenn Werte in der Instance geändert werden. Man muss sich aber den
Programmfluss ansehen, denn Probleme gibt es meist erst dann, wenn ein
schreibender mit einem weiteren Zugriff gleichzeitig erfolgt. Liest man
immer nur, dann brauch man nichts synchronisieren. In main da oben
könntest du problemlos diagram einen neuen Wert zuweisen während
MyRunnable läuft, solange du nicht die Anforderung hast, dass MyRunnable
etwas von dieser Änderung erfahren soll. Denn auch hier hat MyRunnable
seine eigene Kopie und zwar von der Referenz. Da die Instance nur
innerhalb von MyRunnable geändert wird und das nur im EDT läuft, gibt es
auch hier keine Konflikte.

Etwas ähnliches gilt auch für "f", nur das man es da weniger gut sehen kann.
Post by Ernst Baumann
c)
Vielleicht geschieht der Zugriff auf diese Variablen im EDT zeitlich
nein stell es dir eher so vor, dass main nicht endet, weil es noch den
EDT gibt.. die beiden laufen also parallel. Hättest du invokeAndWait
benutzt, dann würde main pausieren bis dein runnable im EDT abgearbeitet
wurde und würde dann weitermachen.
Post by Ernst Baumann
c1)
Die Anweisung f.setVisible(true) im Main-Thread löst den Aufruf von
repaint() aus.
repaint stellt die entsprechenden Anweisungen in den EDT und fordert
_dann_ den EDT-Thread an
c2)
Die Anweisung invokeLater(...) stellt die Anweisungen in run(...) in
den EDT und fordert _dann_ den EDT-Thread an.
Aber wann der EDT ausgeführt wird, bestimmt doch das Betriebssystem.
Deswegen ist mir dies nicht richtig klar!
der EDT fängt dann halt irgendwann zu laufen an. Das ist nicht in ferner
Zukunft, sondern in der Regel recht schnell (innerhalb von ms), aber
einen genauen Zeitpunkt kann man nicht angeben.
Post by Ernst Baumann
2)
a)
Von außerhalb des EDT soll nicht auf bereits realisierte
Swing/AWT-Objekte zugegriffen werden.
ja
Post by Ernst Baumann
Das wird doch aber bei meinem Programm im Main Thread gemacht, z.B.
diagramm
f
jein... gemeint sind nicht die Variablen, sondern Komponenten wie ein
JFrame. Realisiert ist ein JFrame zum Beispiel in etwa nach dem
setVisible(true) Aufruf, aber ab diesem Zeitpunkt machst du ja keine
Änderungen mehr. Zum Beispiel fügst du keine Komponenten hinzu oder
änderst die Farben des JFrame. All das was mit JFrame und Diagramm noch
pasiert erfolgt in deinem Programm im EDT.
Post by Ernst Baumann
b)
Man darf eigentlich gar nicht von außerhalb, also von einem anderen
Thread auf den EDT zugreifen, außer man regelt das mit synchronized.
Ist das richtig?
wie definierst du "auf den EDT zugreifen"?

Gruss theo
Michael Paap
2009-02-26 21:02:23 UTC
Permalink
Post by Jochen Theodorou
nein stell es dir eher so vor, dass main nicht endet, weil es noch den
EDT gibt.. die beiden laufen also parallel.
Das finde ich etwas verwirrend ausgedrückt. Warum sollte ein noch
laufender EDT den Main-Thread hindern, sich zu beenden, wenn er seinen
Job erledigt hat? Ernst könnte den Schluss ziehen, dass dem immer so wäre...

Gruß,
Michael
Jochen Theodorou
2009-02-26 22:31:05 UTC
Permalink
Post by Michael Paap
Post by Jochen Theodorou
nein stell es dir eher so vor, dass main nicht endet, weil es noch den
EDT gibt.. die beiden laufen also parallel.
Das finde ich etwas verwirrend ausgedrückt. Warum sollte ein noch
laufender EDT den Main-Thread hindern, sich zu beenden, wenn er seinen
Job erledigt hat? Ernst könnte den Schluss ziehen, dass dem immer so wäre...
ich hab mich nicht verwirrend ausgedrückt, sondern schlicht falsch.
Irgendiwe dachte ich main wäre immer ein deamon thread.. aber das macht
natürlich eigentlich nicht wirklich Sinn.

Gruss theo
Marcus Woletz
2009-02-26 21:54:00 UTC
Permalink
Hallo Jochen,

Jochen Theodorou schrieb:

[...]
Post by Jochen Theodorou
Post by Ernst Baumann
c)
Vielleicht geschieht der Zugriff auf diese Variablen im EDT zeitlich
nein stell es dir eher so vor, dass main nicht endet, weil es noch den
EDT gibt.. die beiden laufen also parallel. Hättest du invokeAndWait
benutzt, dann würde main pausieren bis dein runnable im EDT abgearbeitet
wurde und würde dann weitermachen.
na ja, genau genommen ist es ja so, dass der main Thread durchaus endet,
auch wenn der EDT noch läuft. Das tut allerdings nicht weh, da die VM
erst beendet wird, wenn der letzte Thread, also entweder der main
Thread, der EDT oder ein anderer Thread, beendet ist.

[...]
Post by Jochen Theodorou
Gruss theo
ciao

Marcus
Jochen Theodorou
2009-02-26 22:31:36 UTC
Permalink
Post by Marcus Woletz
Hallo Jochen,
[...]
Post by Jochen Theodorou
Post by Ernst Baumann
c)
Vielleicht geschieht der Zugriff auf diese Variablen im EDT zeitlich
nein stell es dir eher so vor, dass main nicht endet, weil es noch den
EDT gibt.. die beiden laufen also parallel. Hättest du invokeAndWait
benutzt, dann würde main pausieren bis dein runnable im EDT abgearbeitet
wurde und würde dann weitermachen.
na ja, genau genommen ist es ja so, dass der main Thread durchaus endet,
auch wenn der EDT noch läuft. Das tut allerdings nicht weh, da die VM
erst beendet wird, wenn der letzte Thread, also entweder der main
Thread, der EDT oder ein anderer Thread, beendet ist.
ja stimmt... weiss nicht was mich da geritten hat

Gruss theo
Wolfgang Zitzelsberger
2009-02-27 09:17:27 UTC
Permalink
Post by Ernst Baumann
2)
a)
Von außerhalb des EDT soll nicht auf bereits realisierte
Swing/AWT-Objekte zugegriffen werden.
ja
Da das immer noch in den Köpfen rumschwirrt, möchte ich die Gelegenheit
nutzen, um nochmal darauf hinzuweisen, dass dies seit einiger Zeit nicht
mehr richtig und auch für nicht realisierte Swing Komponenten gilt. Der
Begriff "realisiert" sollte in diesem Zusammenhang eventuell genauer
definiert werden.

Zitat:
"Initially there was a rule that it is safe to create and use Swing
components until they are realized but this rule is not valid any more,
and now it is recommended to interact with Swing from EDT only"

Quelle:
http://weblogs.java.net/blog/alexfromsun/archive/2006/02/debugging_swing.html

Wer Probleme mit dem EDT vermutet (und auch alle anderern ;-)), sollte
seine Anwendung mit dem CheckThreadViolationRepaintManager
(https://swinghelper.dev.java.net/) testen.


Grüße,
Wolfgang
--
www.javasoft.de
Ernst Baumann
2009-02-27 10:45:55 UTC
Permalink
Post by Jochen Theodorou
Hallo Ernst, schon lange keine dieser posts von dir mehr gehabt ;)
das stimmt, ich will euch nicht allzu sehr nerven... :-)
Post by Jochen Theodorou
Diagramm ist nicht primitiv, also könnte es theoretisch Konflikte geben,
wenn Werte in der Instance geändert werden.
Was meinst du mit Werte in der Instance?
Meinst du Werte innerhalb des Objekts? (siehe auch meine Beschreibung
unten)
Also:
Diagramm diagramm = new Diagramm(550, 550);
veranlaßt, dass ein Objekt an einer bestimmten Adresse im
Arbeitsspeicher (z.B. 0100) angelegt wird.
Die Variable diagramm hat dann den Wert 0100 und zeigt damit auf
dieses Objekt.
Mit
diagramm.setOrt(123);
kann man dann über die Adresse 0100 z.B. auf ein Attribut wie z.B.ort
(eine Schublade des Objekts) zugreifen.
Werden "Werte in der Instance geändert" z.B. durch folgende Anwesiung?
diagramm.setOrt(123);
oder was meinst du (lies vielleicht erst meine weiteren Erklärungen
unten bei "ich denke folgendes")
Post by Jochen Theodorou
Man muss sich aber den
Programmfluss ansehen, denn Probleme gibt es meist erst dann, wenn ein
schreibender mit einem weiteren Zugriff gleichzeitig erfolgt. Liest man
immer nur, dann brauch man nichts synchronisieren.
In main da oben
könntest du problemlos diagram einen neuen Wert zuweisen während
MyRunnable läuft, solange du nicht die Anforderung hast, dass MyRunnable
etwas von dieser Änderung erfahren soll.
Meinst du damit z.B. dass ich eine Anweisung wie
Diagramm diagramm = new Diagramm(123, 456);
bringen kann, _nachdem_ in main(...)
MyRunnable myR = new MyRunnable(diagramm, stelle);
javax.swing.SwingUtilities.invokeLater(myR);
aufgerufen wurde?
Post by Jochen Theodorou
Denn auch hier hat MyRunnable
seine eigene Kopie und zwar von der Referenz. Da die Instance nur
innerhalb von MyRunnable geändert wird und das nur im EDT läuft, gibt es
auch hier keine Konflikte.
Ich denke folgendes:
1)
Wenn ich in main(..) die folgende Anweisung mache:
diagramm.setOrt(123);
dann wird in main(...) _und_ im EDT (durch diagramm.setOrt(ort))
auf die Variable
diagramm.ort
zugegriffen, wobei mindestens ein Thread einen Schreibzugriff macht.
Deshalb kann es Problme geben.(Lese-Lesezugriff ist erlaubt,
Schreib-Lesezugriff nicht erlaubt, Schreib-Schreibzugriff nicht
erlaubt))
Auf die Variable diagramm mit dem Wert 0100 wird zwar auch von allen
zwei Threads aus zugegriffen, doch sind das nur Lesezugriffe (d.h. der
Wert 0100 wird _nicht_ verändert), deshalb gibt es keine Probleme.
Ist das richtig?

2)
Wenn eine Anweisung wie (zusätzlich zur alten Anweisung
Diagramm diagramm = new Diagramm(550, 550))
Diagramm diagramm = new Diagramm(123, 456);
in main kommt, _nachdem_
MyRunnable myR = new MyRunnable(diagramm, stelle);
javax.swing.SwingUtilities.invokeLater(myR);
aufgerufen wurde, dann hat digramm z.B. einen neuen Wert, wie z.B.
0200. Das macht aber nichts, weil im EDT der Wert 0100 von diagramm
verwendet wird, der von der alten Anweisung
Diagramm diagramm = new Diagramm(550, 550);
herrührt.
Ist das richtig?

3)
Ein Problem habe ich noch:
Wenn in main die folgende Anweisung
Diagramm diagramm = new Diagramm(550, 550);
noch _nicht_ abgearbeitet wurde, hat diagramm einen undefinierten
Wert.
Wenn jetzt aber der EDT die Anweisung
diagramm.setOrt(ort);
macht, dann gibt es doch auch Probleme.
Ist das richtig?
Post by Jochen Theodorou
Post by Ernst Baumann
2)
a)
Von außerhalb des EDT soll nicht auf bereits realisierte
Swing/AWT-Objekte zugegriffen werden.
ja
Post by Ernst Baumann
Das wird doch aber bei meinem Programm im Main Thread gemacht, z.B.
diagramm
f
jein... gemeint sind nicht die Variablen, sondern Komponenten wie ein
JFrame.
Ja, das sind die von mir bezeichneten Schubladen, also die Attribute.
Es kann Probleme geben, wenn mindestens ein Thread einen
Schreibzugriff darauf ausübt (und ein anderer Thread, wie z.B. der EDT
einen Lesezugriff macht).
Lese-Lesezugriff ist erlaubt, Schreib-Lesezugriff nicht erlaubt,
Schreib-Schreibzugriff nicht erlaubt.
Post by Jochen Theodorou
Realisiert ist ein JFrame zum Beispiel in etwa nach dem
setVisible(true) Aufruf, aber ab diesem Zeitpunkt machst du ja keine
Änderungen mehr. Zum Beispiel fügst du keine Komponenten hinzu oder
änderst die Farben des JFrame. All das was mit JFrame und Diagramm noch
pasiert erfolgt in deinem Programm im EDT.
Kann nicht der Fall wie bei 3) auftreten?
Post by Jochen Theodorou
Post by Ernst Baumann
b)
Man darf eigentlich gar nicht von außerhalb, also von einem anderen
Thread auf den EDT zugreifen, außer man regelt das mit synchronized.
Ist das richtig?
wie definierst du "auf den EDT zugreifen"?
Habe mich falsch ausgedrückt, deshab folgende Korrektur:
Die Aussage bei 2a) müsste eigentlich noch verschärft werden:
Man darf eigentlich gar nicht von außerhalb, also von einem anderen
Thread aus auf gemeinsame - mit dem EDT - genutzte Variablen, also
Speicherbereiche (außer Lese-Lesezugriffe) zugreifen, außer man regelt
das mit synchronized.
Ist das richtig?


mfg
Ernst
Jochen Theodorou
2009-02-27 11:45:11 UTC
Permalink
Post by Ernst Baumann
Post by Jochen Theodorou
Hallo Ernst, schon lange keine dieser posts von dir mehr gehabt ;)
das stimmt, ich will euch nicht allzu sehr nerven... :-)
Post by Jochen Theodorou
Diagramm ist nicht primitiv, also könnte es theoretisch Konflikte geben,
wenn Werte in der Instance geändert werden.
Was meinst du mit Werte in der Instance?
Meinst du Werte innerhalb des Objekts? (siehe auch meine Beschreibung
unten)
ja... für einen Koflikt müssen das aber verschiedene Threads
gleichzeitig versuchen und mindestens einer davon schreibend.
Post by Ernst Baumann
Diagramm diagramm = new Diagramm(550, 550);
veranlaßt, dass ein Objekt an einer bestimmten Adresse im
Arbeitsspeicher (z.B. 0100) angelegt wird.
Die Variable diagramm hat dann den Wert 0100 und zeigt damit auf
dieses Objekt.
Im Prinzip ja, aber ehrlich gesagt hat das hier schon ewige Diskussionen
ausgelöst die ich nur ungern wachrütteln würde. Man spricht von einer
Reference in Java, und wie die genau realisiert ist sollte nicht so sehr
interessieren.

[...]
Post by Ernst Baumann
Werden "Werte in der Instance geändert" z.B. durch folgende Anwesiung?
diagramm.setOrt(123);
ja

[...]
Post by Ernst Baumann
Meinst du damit z.B. dass ich eine Anweisung wie
Diagramm diagramm = new Diagramm(123, 456);
bringen kann, _nachdem_ in main(...)
MyRunnable myR = new MyRunnable(diagramm, stelle);
javax.swing.SwingUtilities.invokeLater(myR);
aufgerufen wurde?
ja. Die MyRunnable, die schon im EDT sind werden dann aber nur den alten
Wert von diagramm sehen kennen (weil die Reference kopiert wurde)

[...]
Post by Ernst Baumann
1)
diagramm.setOrt(123);
dann wird in main(...) _und_ im EDT (durch diagramm.setOrt(ort))
auf die Variable
diagramm.ort
zugegriffen, wobei mindestens ein Thread einen Schreibzugriff macht.
entscheidend ist die Gleichzeitigkeit Ist garantiert dass alle
setOrt(ort) nach setOrt(123) ausgeführt werden, dann gibt es keinen
Konflikt. Naja, eigentlich wird die Sache erst interessant wenn es auch
Lese zugriffe gibt. Sagen wir, wir haben 2 Threads T1 und T2 und beide
machen setOrt und dann getOrt. T1 mit dem Wert 1, T2 mit dem Wert 2
Post by Ernst Baumann
T_1 T_2
| |
setOrt(1) setOrt(2)
| |
x=getOrt() y=getOrt()
Das soll andeuten beide Threads laufen gleichzeitig. Die Frage ist nun
was eigentlich in x und y drin steht. Dazu überführt man diese zwei
Post by Ernst Baumann
setOrt(1) setOrt(1)
| |
setOrt(2) setOrt(2)
| |
x=getOrt() bzw. y=getOrt()
| |
y=getOrt() x=getOrt()
hier haben x und y den Wert 2, weil setOrt(2) eine 2 schrieb
Post by Ernst Baumann
setOrt(2) setOrt(2)
| |
setOrt(1) setOrt(1)
| |
x=getOrt() bzw. y=getOrt()
| |
y=getOrt() x=getOrt()
hier haben x und y den Wert 1, weil setOrt(1) eine 1 schrieb
Post by Ernst Baumann
setOrt(1)
|
x=getOrt()
|
setOrt(2)
|
y=getOrt()
x==1, y==2
Post by Ernst Baumann
setOrt(2)
|
y=getOrt()
|
setOrt(1)
|
x=getOrt()
x==1, y==2

S5:
und als letztes ist noch möglich, dass T1 und T2 jeweils ihre eigenen
Versionen des Feldes sehen werden, dann ergibt sich auch x==1 und y==2.
Wäre das Feld ein long oder double würden sich noch sehr viel mehr Fälle
ergeben. In diesen Zusatzfällen würde dann im Feld letzlich Müll stehen,
wenn die setter entsprechend große Zahlen bekommen

[...]
Post by Ernst Baumann
Auf die Variable diagramm mit dem Wert 0100 wird zwar auch von allen
zwei Threads aus zugegriffen, doch sind das nur Lesezugriffe (d.h. der
Wert 0100 wird _nicht_ verändert), deshalb gibt es keine Probleme.
Ist das richtig?
ja
Post by Ernst Baumann
2)
Wenn eine Anweisung wie (zusätzlich zur alten Anweisung
Diagramm diagramm = new Diagramm(550, 550))
Diagramm diagramm = new Diagramm(123, 456);
in main kommt, _nachdem_
MyRunnable myR = new MyRunnable(diagramm, stelle);
javax.swing.SwingUtilities.invokeLater(myR);
aufgerufen wurde, dann hat digramm z.B. einen neuen Wert, wie z.B.
0200. Das macht aber nichts, weil im EDT der Wert 0100 von diagramm
verwendet wird, der von der alten Anweisung
Diagramm diagramm = new Diagramm(550, 550);
herrührt.
Ist das richtig?
ja
Post by Ernst Baumann
3)
Wenn in main die folgende Anweisung
Diagramm diagramm = new Diagramm(550, 550);
noch _nicht_ abgearbeitet wurde, hat diagramm einen undefinierten
Wert.
aber das ist ein unmöglicher Fall, denn du übergibst ja diagramm an
MyRunnable und zu diesem Zeitpunkt muss diagramm initialisiert sein.
Einen undefinierten Wert hat diagramm eigentlich nie.

[...]
Post by Ernst Baumann
Post by Jochen Theodorou
Post by Ernst Baumann
b)
Man darf eigentlich gar nicht von außerhalb, also von einem anderen
Thread auf den EDT zugreifen, außer man regelt das mit synchronized.
Ist das richtig?
wie definierst du "auf den EDT zugreifen"?
Man darf eigentlich gar nicht von außerhalb, also von einem anderen
Thread aus auf gemeinsame - mit dem EDT - genutzte Variablen, also
Speicherbereiche (außer Lese-Lesezugriffe) zugreifen, außer man regelt
das mit synchronized.
Ist das richtig?
Also wenn du einen Schreibzugriff gefolgt von einem Lesezugriff hast und
du sicherstellen willst, dass in der Zwischenzeit kein anderer
Schreibzugriff erfolgt, dann müssen beide im gleichen synchronized block
(direkt oder indirekt) sein. Es reicht nicht dafür zu sorgen dass alle
Lese- und Schreibzugriffe für sich in solch einem Block sind, denn dann
bekommst du trotzdem Fälle wie oben S1-S4, nur S5 kann dann nicht
passieren.

Gruss theo
Ernst Baumann
2009-02-27 16:31:56 UTC
Permalink
Post by Jochen Theodorou
Post by Ernst Baumann
diagramm.setOrt(123);
dann wird in main(...) _und_ im EDT (durch diagramm.setOrt(ort))
auf die Variable
diagramm.ort
zugegriffen, wobei mindestens ein Thread einen Schreibzugriff macht.
entscheidend ist die Gleichzeitigkeit
Müsste es bei _einer einzigen_ CPU nicht "Fastgleichzeitigkeit"
heissen, weil eine CPU zu einem bestimmten Zeitpunkt nur genau einen
Assemblerbefehl abarbeiteen kann (zumindest der 8086 Intel Prozessor
oder ganz primitive Prozessoren).
Entscheidend ist doch, dass Programmteile (= Anweisungsfolgen), die
auf bestimmten Daten ausgeführt werden (egal, in welcher zeitlichen
Reihenfolge, also parallel oder sequentiell) keine Dateninkonsistenz
zur Folge haben.
Oder wie heisst der korrekte Ausdruck statt Gleichzeitigkeit?
Post by Jochen Theodorou
x==1, y==2
Post by Ernst Baumann
setOrt(2)
|
y=getOrt()
|
setOrt(1)
|
x=getOrt()
x==1, y==2
und als letztes ist noch möglich, dass T1 und T2 jeweils ihre eigenen
Versionen des Feldes sehen werden, dann ergibt sich auch x==1 und y==2.
Fall S1 bus S4 habe ich verstanden, aber nicht S5.
Kannst du das ausführlicher erklären?
Post by Jochen Theodorou
Post by Ernst Baumann
3)
Wenn in main die folgende Anweisung
Diagramm diagramm = new Diagramm(550, 550);
noch _nicht_ abgearbeitet wurde, hat diagramm einen undefinierten
Wert.
aber das ist ein unmöglicher Fall, denn du übergibst ja diagramm an
MyRunnable und zu diesem Zeitpunkt muss diagramm initialisiert sein.
Einen undefinierten Wert hat diagramm eigentlich nie.
Du hast recht, denn:
invokeLater weist den EDT an, die run()-Methode des übergebenen
Runnables (also myR) abzuarbeiten.
Und wenn der EDT schon arbeitet, _bevor_ der main-Thread beginnt, dann
kann er noch nicht die run()-Methode des übergebenen Runnables
abarbeiten (und also noch nicht auf diagramm und die Attribute von
diagramm zugreifen), sondern muss etwas anderes machen (z.B. einen
Listenhandler abarbeiten oder ein Fenster zeichnen oder eben gar
nichts machen und warten).
Ist meine Erklärung richtig?
Post by Jochen Theodorou
Post by Ernst Baumann
Man darf eigentlich gar nicht von außerhalb, also von einem anderen
Thread aus auf gemeinsame - mit dem EDT - genutzte Variablen, also
Speicherbereiche (außer Lese-Lesezugriffe) zugreifen, außer man regelt
das mit synchronized.
Ist das richtig?
Also wenn du einen Schreibzugriff gefolgt von einem Lesezugriff hast und
du sicherstellen willst, dass in der Zwischenzeit kein anderer
Schreibzugriff erfolgt, dann müssen beide im gleichen synchronized block
(direkt oder indirekt) sein. Es reicht nicht dafür zu sorgen dass alle
Lese- und Schreibzugriffe für sich in solch einem Block sind, denn dann
bekommst du trotzdem Fälle wie oben S1-S4, nur S5 kann dann nicht
passieren.
Ich habe dich leider nicht verstanden. Deswegen das folgende Besipiel.
In dem Beispiel kann doch in dem mit
// schützen
bezeichneten Bereich doch nur immer genau ein Thread zugreifen und
damit ist das Problem gelöst. Oder wo ist mein Denkfehler?



------Beispiel------------------------
main()
//schützen
Anweisung_1, die Swing verwendet
//Ende schützen

i=10;
i=11;
i=12 oder irgendeine andere Nicht-Swing-Anweisung

//schützen
Anweisung_2, die Swing verwendet
//Ende schützen
-----------------------------------

mfg
Ernst
Jochen Theodorou
2009-02-27 18:00:10 UTC
Permalink
Post by Ernst Baumann
Post by Jochen Theodorou
Post by Ernst Baumann
diagramm.setOrt(123);
dann wird in main(...) _und_ im EDT (durch diagramm.setOrt(ort))
auf die Variable
diagramm.ort
zugegriffen, wobei mindestens ein Thread einen Schreibzugriff macht.
entscheidend ist die Gleichzeitigkeit
Müsste es bei _einer einzigen_ CPU nicht "Fastgleichzeitigkeit"
Ich meine das schonmal als quasisequentiell gehört zu haben
Post by Ernst Baumann
heissen, weil eine CPU zu einem bestimmten Zeitpunkt nur genau einen
Assemblerbefehl abarbeiteen kann (zumindest der 8086 Intel Prozessor
oder ganz primitive Prozessoren).
Die werden aber immer seltener ;) Aber die meisten Dinge, die man in
Java macht bestehen nunmal aus mehreren Assemblerbefehlen.
Post by Ernst Baumann
Entscheidend ist doch, dass Programmteile (= Anweisungsfolgen), die
auf bestimmten Daten ausgeführt werden (egal, in welcher zeitlichen
Reihenfolge, also parallel oder sequentiell) keine Dateninkonsistenz
zur Folge haben.
keine Dateninkonsistenzen zu haben (oder weleche, die nicht stören) ist
das Ziel, ja.
Post by Ernst Baumann
Post by Jochen Theodorou
x==1, y==2
Post by Ernst Baumann
setOrt(2)
|
y=getOrt()
|
setOrt(1)
|
x=getOrt()
x==1, y==2
und als letztes ist noch möglich, dass T1 und T2 jeweils ihre eigenen
Versionen des Feldes sehen werden, dann ergibt sich auch x==1 und y==2.
Fall S1 bus S4 habe ich verstanden, aber nicht S5.
Kannst du das ausführlicher erklären?
S5 kann nur in Mehrprozessorsystemen vorkommen. Normalerweise hat jeder
Prozessor einen kleinen Cache und dieser Zeigt Auszüge aus dem
Speicher... oder anders gesagt Objekte und die Werte der Attribute. Das
heisst aber auch dss jeder Prozessor seine eigene Sichtweise dieser
Daten hat. Werden die Daten in einem Cache verändert, weiss der andere
davon erstmal nichts. Erst nachdem die speicher synchronisiert wurden
sehen wieder alle Prozessoren das Gleiche. Bis dahin kann es aber
vorkommen dass ein Prozessor ein anderes "ort" sieht als der andere.

Der Fall ist eher selten, habe ich aber selbst schon gesehen.
Post by Ernst Baumann
Post by Jochen Theodorou
Post by Ernst Baumann
3)
Wenn in main die folgende Anweisung
Diagramm diagramm = new Diagramm(550, 550);
noch _nicht_ abgearbeitet wurde, hat diagramm einen undefinierten
Wert.
aber das ist ein unmöglicher Fall, denn du übergibst ja diagramm an
MyRunnable und zu diesem Zeitpunkt muss diagramm initialisiert sein.
Einen undefinierten Wert hat diagramm eigentlich nie.
invokeLater weist den EDT an, die run()-Methode des übergebenen
Runnables (also myR) abzuarbeiten.
Und wenn der EDT schon arbeitet, _bevor_ der main-Thread beginnt, dann
kann er noch nicht die run()-Methode des übergebenen Runnables
abarbeiten (und also noch nicht auf diagramm und die Attribute von
diagramm zugreifen), sondern muss etwas anderes machen (z.B. einen
Listenhandler abarbeiten oder ein Fenster zeichnen oder eben gar
nichts machen und warten).
Ist meine Erklärung richtig?
ja
Post by Ernst Baumann
Post by Jochen Theodorou
Post by Ernst Baumann
Man darf eigentlich gar nicht von außerhalb, also von einem anderen
Thread aus auf gemeinsame - mit dem EDT - genutzte Variablen, also
Speicherbereiche (außer Lese-Lesezugriffe) zugreifen, außer man regelt
das mit synchronized.
Ist das richtig?
Also wenn du einen Schreibzugriff gefolgt von einem Lesezugriff hast und
du sicherstellen willst, dass in der Zwischenzeit kein anderer
Schreibzugriff erfolgt, dann müssen beide im gleichen synchronized block
(direkt oder indirekt) sein. Es reicht nicht dafür zu sorgen dass alle
Lese- und Schreibzugriffe für sich in solch einem Block sind, denn dann
bekommst du trotzdem Fälle wie oben S1-S4, nur S5 kann dann nicht
passieren.
Ich habe dich leider nicht verstanden. Deswegen das folgende Besipiel.
In dem Beispiel kann doch in dem mit
// schützen
bezeichneten Bereich doch nur immer genau ein Thread zugreifen und
damit ist das Problem gelöst. Oder wo ist mein Denkfehler?
was ich meinte ist der normale Code:

diagram.setOrt(x)
y=diagram.getOrt()

und hat das mit verschiedenen x in mehreren gleichzeitig laufenden
Threads, dann ist unklar welchen Wert y haben wird. Will man das
absichern und tut das so:

synchronized { diagram.setOrt(x) }
synchronized { y=diagram.getOrt() }

dann kann sich ja noch immer ein setOrt vor ein getOrt schieben. Nur
wenn man das so macht:

synchronized {
diagram.setOrt(x)
y=diagram.getOrt()
}

kann man sicherstellen dass das read und write zusammenpassen

Gruss theo
Ernst Baumann
2009-02-28 09:33:09 UTC
Permalink
I)
Post by Jochen Theodorou
Post by Ernst Baumann
Ich habe dich leider nicht verstanden. Deswegen das folgende Besipiel.
In dem Beispiel kann doch in dem mit
// schützen
bezeichneten Bereich doch nur immer genau ein Thread zugreifen und
damit ist das Problem gelöst. Oder wo ist mein Denkfehler?
diagram.setOrt(x)
y=diagram.getOrt()
und hat das mit verschiedenen x in mehreren gleichzeitig laufenden
Threads, dann ist unklar welchen Wert y haben wird. Will man das
synchronized { diagram.setOrt(x) }
synchronized { y=diagram.getOrt() }
dann kann sich ja noch immer ein setOrt vor ein getOrt schieben. Nur
synchronized {
diagram.setOrt(x)
y=diagram.getOrt()
}
kann man sicherstellen dass das read und write zusammenpassen
Gut, dann muss man eben ganze Blöcke vor quasigleichzeitigen
Mehrfachzugriffen schützen, in der Form wie du es machst.

Deshab nochmals folgende Korrektur von mir:
Man darf eigentlich gar nicht von außerhalb, also von einem anderen
Thread aus auf gemeinsame - mit dem EDT - genutzte Variablen, also
Speicherbereiche (außer Lese-Lesezugriffe) zugreifen, außer man regelt
das mit synchronized _und_ schützt ganze Blöcke.
Das wäre also eine Alternative zum Zugriff auf Swingkomponenten im EDT
Ist das richtig?

II)
Das folgende Programmm ist fast das Gleiche, wie in meinem
Ursprungsposting, nur dass ich dort nicht das Bild (image) in
paintComponent auf dem Bildschirm ausgebe, sondern direkt die Kreise
auf dem Bildschirm ausgebe (ohne den Umweg über image).

Frage:
Warum gehen dort bei der gleichen Verzögerung mit sleep Bilder
verloren, wohingegen bei der Ausgabe des images - wie in meinem
Ursprungsposting - keine Bilder verlorengehen? (Zum Verständnis siehe
auch unten III)

-----------------------------------------------------------------
package verzoegertzeichnen5;

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

public class MainVerzoegertZeichnen5 {
public static void main(String[] args){
int stelle;
stelle = 1;
Diagramm diagramm = new Diagramm(550, 550);
JFrame f = new JFrame();
f.setSize(550,550);
f.getContentPane().add(diagramm);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);

while(stelle<=10){
System.out.println("stelle in main="+stelle);
try{
Thread.sleep(10);
}
catch(Exception e){}
stelle=stelle+1;

MyRunnable myR = new MyRunnable(diagramm, stelle);
javax.swing.SwingUtilities.invokeLater(myR);


}
}
}

class Diagramm extends JPanel{
private int xpAnz;
private int ypAnz;
private int ort;
private Image myimg;
private Graphics myg;
private int sx;
private int sy;

public Diagramm(int xpAnz, int ypAnz){
ort=0;
this.xpAnz=xpAnz;
this.ypAnz=ypAnz;
myimg=null;
}

public void setOrt(int pOrt){
ort=pOrt;
}


// ist im EDT (siehe Methode void run)
public void male(){
if(myimg==null){
sx = this.getSize().width;
System.out.println("sx="+sx);
System.out.println("sy="+sy);

sy = this.getSize().height;
myimg = createImage(sx, sy);
myg = myimg.getGraphics();
}
// Schreibzugriff auf myimg
myg.setColor(Color.red);
myg.drawOval(ort*20 , ort*20, 20, 20);
}

// ist im EDT
public void paintComponent(Graphics g){
System.out.println("paintComponent");
g.setColor(Color.red);
g.drawOval(ort*20 , ort*20, 20, 20);
System.out.println("ort in paintComponent="+ort);

}
}

class MyRunnable implements Runnable{
private Diagramm diagramm;
private int ort;
public void run(){
// ist im EDT
// Schreibzugriff auf ort
diagramm.setOrt(ort);
// Schreibzugriff auf mying
diagramm.male();
// Lesezugriff auf ort in paintComponent
diagramm.repaint();
}

public MyRunnable(Diagramm pDiagramm, int pOrt){
diagramm = pDiagramm;
ort = pOrt;
}
}
-----------------------------------------------------------------

III)
1) coalescing und Zwischenbuffer
Im EDT soll eine Schleife durchlaufen und in dieser ein Malauftrag
(mit repaint()) in die EDT-Warteschlange gestellt werden.
Diese Malaufträge können von der JVM durch coalescing zusammengefasst
werden, d.h. es wird nur der letzte (aktuellste) abgearbeitet.
Dann wird die Methode paintComponent() aufgerufen.
Alle Bildschirmzeichnungen, die dort gemacht werden sollen, werden
zuerst in einen Zwischenbuffer geschrieben und erst wenn
paintComponent beendet wird, wird der Inhalt des Zwischenbuffers auf
dem Bildschirm augegeben.
Im folgenden Beispiel werden also zuerst 1000 Kreise in den
Zwischenbuffer geschrieben und dann erst auf dem Bildschirm
ausgegeben.
for(int i=0; i<1000; i++){
g.drawOval(i , i, 20, 20);
}

Fazit:
Zuerst wird also zusammengefasst (coalescing) und erst dann der
Zwischenbuffer benutzt.
Ist das richtig beschrieben?

2)
Bei einer Veränderung des Fensters (z.B. durch den Anwender, der das
Fenster verschiebt) wird der Zwischenbuffer automatisch (ohne Zutun
des Programmierers) ungültig, also gelöscht.
Deswegen ist es besser, ein Image zu benutzen (mit createImage)
Ist das richtig beschrieben?

mfg
Ernst
Jochen Theodorou
2009-02-28 20:15:43 UTC
Permalink
Ernst Baumann schrieb:
[...]
Post by Ernst Baumann
Gut, dann muss man eben ganze Blöcke vor quasigleichzeitigen
Mehrfachzugriffen schützen, in der Form wie du es machst.
das kommt wie gesagt immer auf den Zugriff und die Nebenbedingungen an.
Post by Ernst Baumann
Man darf eigentlich gar nicht von außerhalb, also von einem anderen
Thread aus auf gemeinsame - mit dem EDT - genutzte Variablen, also
Speicherbereiche (außer Lese-Lesezugriffe) zugreifen, außer man regelt
das mit synchronized _und_ schützt ganze Blöcke.
Das wäre also eine Alternative zum Zugriff auf Swingkomponenten im EDT
Ist das richtig?
nur geht das nicht, weil Swing kein synchronized benutzt

[...]
Post by Ernst Baumann
III)
1) coalescing und Zwischenbuffer
Im EDT soll eine Schleife durchlaufen und in dieser ein Malauftrag
(mit repaint()) in die EDT-Warteschlange gestellt werden.
Diese Malaufträge können von der JVM durch coalescing zusammengefasst
werden, d.h. es wird nur der letzte (aktuellste) abgearbeitet.
na das würde ja schon mal erklären warum Uwischenbilder verloren gehen
können... Wenn du direkt zeichnest, dann wird das ja auf eine Fläche
gezeichnet, die durch repaint vorbereitet wurde. Folglich bleibt der
Malvorgang aus, wenn das repaint nicht durchgeführt wird. Im Original
zeichnest ja in ein Bild und das Zeichnen wir von deinem runnable durch
.male() ausgelöst. male() wird aber jedesmal ausgeführt, wärend
paintComponentvielleicht ausbleibt.
Post by Ernst Baumann
Dann wird die Methode paintComponent() aufgerufen.
Alle Bildschirmzeichnungen, die dort gemacht werden sollen, werden
zuerst in einen Zwischenbuffer geschrieben und erst wenn
paintComponent beendet wird, wird der Inhalt des Zwischenbuffers auf
dem Bildschirm augegeben.
Im folgenden Beispiel werden also zuerst 1000 Kreise in den
Zwischenbuffer geschrieben und dann erst auf dem Bildschirm
ausgegeben.
for(int i=0; i<1000; i++){
g.drawOval(i , i, 20, 20);
}
Zuerst wird also zusammengefasst (coalescing) und erst dann der
Zwischenbuffer benutzt.
Ist das richtig beschrieben?
mit Zwischenbuffer oder ohne... das Malen erfolgt erst nach dem
Zusammenfassen von repaint-Aufrufen, ja.
Post by Ernst Baumann
2)
Bei einer Veränderung des Fensters (z.B. durch den Anwender, der das
Fenster verschiebt) wird der Zwischenbuffer automatisch (ohne Zutun
des Programmierers) ungültig, also gelöscht.
ob der wirklich gelöscht wird kann ich nicht so genau sagen, aber wenn
ein repaint dadurch notwendig wird, dann ist er zumindest ungülig, ja.
Post by Ernst Baumann
Deswegen ist es besser, ein Image zu benutzen (mit createImage)
Ist das richtig beschrieben?
Das kommt auf den konkreten Fall an. Wenn du einfach nur den momentanen
Zustand zeichnen willst, dann braucht man das nicht, allerdings gehören
bei dir alle vorherigen Kreise dazu. Und wenn du alle zeichnest, die bis
dahin dargestellt werden sollen, dann kann natürlich auch keiner
verloren gehen.

Gruss theo
Ernst Baumann
2009-03-01 10:48:39 UTC
Permalink
Post by Jochen Theodorou
Post by Ernst Baumann
Man darf eigentlich gar nicht von außerhalb, also von einem anderen
Thread aus auf gemeinsame - mit dem EDT - genutzte Variablen, also
Speicherbereiche (außer Lese-Lesezugriffe) zugreifen, außer man regelt
das mit synchronized _und_ schützt ganze Blöcke.
Das wäre also eine Alternative zum Zugriff auf Swingkomponenten im EDT
Ist das richtig?
nur geht das nicht, weil Swing kein synchronized benutzt
Wenn Swing _intern_ _kein_ synchronized benutzt (Swing ist nicht
threadsicher), dann kann man dies als Programmierer doch dadurch
kompensieren, dass man _extern_ synchronized benutzt.
-----Beispiel------------------------
main()
//schützen
Lauter Anweisungen, in denen Swing verwendet wird
//Ende schützen

Anweisung_1, in denen kein Swing verwendet wird
Anweisung_2, in denen kein Swing verwendet wird
...
Anweisung_n, in denen kein Swing verwendet wird
-----------------------------------
Wo ist mein Denkfehler?
Post by Jochen Theodorou
na das würde ja schon mal erklären warum Uwischenbilder verloren gehen
können... Wenn du direkt zeichnest, dann wird das ja auf eine Fläche
gezeichnet, die durch repaint vorbereitet wurde. Folglich bleibt der
Malvorgang aus, wenn das repaint nicht durchgeführt wird. Im Original
zeichnest ja in ein Bild und das Zeichnen wir von deinem runnable durch
.male() ausgelöst. male() wird aber jedesmal ausgeführt, wärend
paintComponentvielleicht ausbleibt.
Stimmt, so weit hatte ich nicht gedacht!
Was passiert bei meinem Programm im Ursprungsposting, wenn ein
paintComponentvielleicht ausbleibt?
Dann befindet sich immer noch das Bild vom letzten paintComponent
(z.B.Kreise 1-3) auf dem Bildschirm und dieses Bild ist korrekt, weil
in das Bild myimg bei jedem Schleifendurchgang der neue Kreis
hinzugezeichnet wird.
Wenn von nun ab aber dann das paintComponent z.B. genau 2 Mal
hintereinander ausfällt, wird 2 Mal noch das alte Bild (Kreise 1-3)
auf dem Bildschirm gebracht. D.h. 2 Mal sieht man nicht die aktuellen
Bilder Kreise 1-4 und dann Kreis 1-5. Erst danach sieht man wieder die
aktuellen Bild Kreise 1-5, Kreise 1-6, (Kreise 1-7, usw.
Also: _nur_ während dieser kurzen Zeitspanne sieht man nicht die
aktuellen Bilder, sonst wird alles korrekt dargestellt.
_Weil_ diese Zeitspanne so kurz ist (weil die Verzögerung in sleep so
klein gewählt wurde) entdeckt dies der Anwender nicht.
Hätte er sehr, sehr gute Augen, wäre ihm das aufgefallen.
Bei meinem Programm im letzten Posting fällt das dagegen jedem
Anwender auf, weil die verlorenen Kreise bei einem neuen repaint nicht
wieder gezeichnet werden.
Ist das richtig?
Post by Jochen Theodorou
Post by Ernst Baumann
2)
Bei einer Veränderung des Fensters (z.B. durch den Anwender, der das
Fenster verschiebt) wird der Zwischenbuffer automatisch (ohne Zutun
des Programmierers) ungültig, also gelöscht.
ob der wirklich gelöscht wird kann ich nicht so genau sagen, aber wenn
ein repaint dadurch notwendig wird, dann ist er zumindest ungülig, ja.
Gibt es außer einer Veränderung des Fensters (z.B. durch den Anwender,
der das Fenster verschiebt) noch eine Möglichkeit, wo der
Zwischenbuffer ungültig wird?
Post by Jochen Theodorou
Post by Ernst Baumann
Deswegen ist es besser, ein Image zu benutzen (mit createImage)
Ist das richtig beschrieben?
Das kommt auf den konkreten Fall an. Wenn du einfach nur den momentanen
Zustand zeichnen willst, dann braucht man das nicht,
Stimmt, dann ist es egal ob in der Vergangenheit Kreise verloren
gegangen sind und ob bei einer Veränderung des Fensters (z.B. durch
den Anwender, der das Fenster verschiebt) Kreise verloren gingen
Ist das richtig?

mfg
Ernst
Marcus Woletz
2009-03-01 18:38:50 UTC
Permalink
Hallo Ernst,

Ernst Baumann schrieb:
[...]
Post by Ernst Baumann
Wenn Swing _intern_ _kein_ synchronized benutzt (Swing ist nicht
threadsicher), dann kann man dies als Programmierer doch dadurch
kompensieren, dass man _extern_ synchronized benutzt.
-----Beispiel------------------------
main()
//schützen
Lauter Anweisungen, in denen Swing verwendet wird
//Ende schützen
Das nützt doch nichts! Du schützt damit nur Deine paar Anweisungen. Das
ist nicht des Pudels Kern, sondern der Umstand, dass Du in main(), also
im main Thread auf Swing-Komponenten zugreifst. Und das ist bei
ordentlicher Programmierung tabu. Packe also, wie bereits mehrfach
erwähnt, die Zugriffe auf Komponenten einfach in ein Runnable und
übergib das an invokeAndWait() oder invokeLater(). In den vielen
Beispielen wird dann eine statische Funktion "createAndShowGUI()"
aufgerufen. Google mal danach, dann müsstest Du Seiten zu diesem Thema
finden.

[...]

ciao

Marcus
Ernst Baumann
2009-03-02 08:20:58 UTC
Permalink
Post by Marcus Woletz
...
Packe also, wie bereits mehrfach
erwähnt, die Zugriffe auf Komponenten einfach in ein Runnable und
übergib das an invokeAndWait() oder invokeLater(). In den vielen
Beispielen wird dann eine statische Funktion "createAndShowGUI()"
aufgerufen. Google mal danach, dann müsstest Du Seiten zu diesem Thema
finden.
1)
Ich habe mein Programm so gemacht (siehe meine Postings), wie mir hier
empfohlen wurde (mit invokeLater). Das ist alles ok und funktioniert
problemlos...

2)
Post by Marcus Woletz
Post by Ernst Baumann
Wenn Swing _intern_ _kein_ synchronized benutzt (Swing ist nicht
threadsicher), dann kann man dies als Programmierer doch dadurch
kompensieren, dass man _extern_ synchronized benutzt.
-----Beispiel------------------------
main()
//schützen
Lauter Anweisungen, in denen Swing verwendet wird
//Ende schützen
...
Das nützt doch nichts! Du schützt damit nur Deine paar Anweisungen. Das
ist nicht des Pudels Kern, sondern der Umstand, dass Du in main(), also
im main Thread auf Swing-Komponenten zugreifst. Und das ist bei
ordentlicher Programmierung tabu.
... nur will ich einfach wissen, warum man die konkurrierenden
Swing-Teile nicht voneinander mit synchronized schützen kann.
Dazu Folgendes:

a)
In "Java ist auch eine Insel" unter
http://openbook.galileocomputing.de/javainsel/java-16.htm#t316
steht: Swing ist nicht threadsicher.
Es wird dazu folgendes Beispielprogramm geliefert:
-----------------------------------------------------
import javax.swing.*;
public class SwingNoSyncDemo{
public static void main( String args[] ){
final DefaultListModel model = new DefaultListModel();
JList list = new JList( model );
JFrame frame = new JFrame();
frame.getContentPane().add( list );
frame.setSize( 200, 100 );
frame.show();

new Thread(){ public void run() {
setPriority( Thread.MIN_PRIORITY );
while ( true )
model.addElement( "Dumm gelaufen" );
}
}.start();

new Thread(){ public void run() {
setPriority( Thread.MIN_PRIORITY );
while ( true )
model.removeElement( "Dumm gelaufen" );
}
}.start();
}
}
-----------------------------------------------------
Ergebnis:
Beim Ablauf des Programms kommen die Meldungen:
Exception occurred during event dispatching:
java.lang.ArrayIndexOutOfBoundsException: 4145 >=
4145
Also gibt es Probleme!

b) Deswegen habe ich die kritischen Teile geschützt:
while ( true )
model.addElement( "Dumm gelaufen" );
und
while ( true )
model.removeElement( "Dumm gelaufen" );
durch ein synchronized voneinander schützen.

Das Programm dazu:
-----------------------------------------------------
package testsych1;
import javax.swing.*;

public class MainTestSynch1{
public static Object MyLockObjekt = new Object();
public static void main( String args[] ){
final DefaultListModel model = new DefaultListModel();
JList list = new JList( model );
JFrame frame = new JFrame();
frame.getContentPane().add( list );
frame.setSize( 200, 100 );
frame.show();
new Thread(){ public void run() {
setPriority( Thread.MIN_PRIORITY );
synchronized(MyLockObjekt){
while ( true ){
System.out.println("Add-Thread");
model.addElement( "Dumm gelaufen" );
}
}
}
}.start();

new Thread(){ public void run() {
setPriority( Thread.MIN_PRIORITY );
synchronized(MyLockObjekt){
while ( true ){
model.removeElement( "Dumm gelaufen" );
System.out.println("Remove-Thread");
}
}
}
}.start();
}
}
-----------------------------------------------------
Ergebnis:
Es wird immer nur der Remove-Thread ausgeführt. Das ist das Problem!
(vermutlich weil dieser Thread nicht unterbrochen werden darf, weil er
ja mit synchronized geschützt wurde).
Man hat das Problem also nur, weil es Endlosschleifen gibt.
Ist das richtig?

c)
Bei meinem früheren Postings gibt es keine Endlosschleifen.
Deshalb könnte ich doch "theoretisch" als Alternative (z.B. zu meinem
Programm in meinem Ursprungsposting) ein Programm schreiben, das mit
synchronized arbeitet und also auf die Empfehlung ("Packe also, wie
bereits mehrfach erwähnt, die Zugriffe auf Komponenten einfach in ein
Runnable und ...") verzichten.
Ist das richtig oder wo ist mein Denkfehler?

mfg
Ernst
Sascha Broich
2009-03-02 09:32:59 UTC
Permalink
Post by Ernst Baumann
synchronized(MyLockObjekt){
while ( true ){
System.out.println("Add-Thread");
model.addElement( "Dumm gelaufen" );
}
}
synchronized(MyLockObjekt){
while ( true ){
model.removeElement( "Dumm gelaufen" );
System.out.println("Remove-Thread");
}
}
-----------------------------------------------------
Es wird immer nur der Remove-Thread ausgeführt. Das ist das Problem!
(vermutlich weil dieser Thread nicht unterbrochen werden darf, weil er
ja mit synchronized geschützt wurde).
Du synchronisierst hier die gesamte Schleife, daher dein Problem.
Versuchs mal mit

while(true) {
synchronized(MyLockObject) {
model.addElement("Dumm gelaufen"));
}
}

Dann dürfte das schon etwas anders aussehen.
Post by Ernst Baumann
Man hat das Problem also nur, weil es Endlosschleifen gibt.
Ist das richtig?
Nein, das Problem hast du durch die zu weit gefasste Synchronisierung.
Post by Ernst Baumann
c)
Bei meinem früheren Postings gibt es keine Endlosschleifen.
Deshalb könnte ich doch "theoretisch" als Alternative (z.B. zu meinem
Programm in meinem Ursprungsposting) ein Programm schreiben, das mit
synchronized arbeitet und also auf die Empfehlung ("Packe also, wie
bereits mehrfach erwähnt, die Zugriffe auf Komponenten einfach in ein
Runnable und ...") verzichten.
Ist das richtig oder wo ist mein Denkfehler?
Wenn du den Synchronisationsbereich so klein wie möglich hältst, ist das
durchaus möglich.
Wobei ich - aus Erfahrung heraus - für die wechselseitige Abarbeitung
konkurrierender Aktionen eine entsprechende Queue mit Tasks empfehle.

Wirf dazu mal einen Blick auf SwingWorker, bzw., wenn du es von Swing
unabhängig haben möchtest, auf java.util.concurrent.ThreadPoolExecutor, der
vom SwingWorker verwendet wird.


Sascha Broich
--
Schlage nie jemand grundlos.
Ein Grund findet sich immer.
Jochen Theodorou
2009-03-02 13:43:32 UTC
Permalink
Post by Sascha Broich
Post by Ernst Baumann
synchronized(MyLockObjekt){
while ( true ){
System.out.println("Add-Thread");
model.addElement( "Dumm gelaufen" );
}
}
synchronized(MyLockObjekt){
while ( true ){
model.removeElement( "Dumm gelaufen" );
System.out.println("Remove-Thread");
}
}
-----------------------------------------------------
Es wird immer nur der Remove-Thread ausgeführt. Das ist das Problem!
(vermutlich weil dieser Thread nicht unterbrochen werden darf, weil er
ja mit synchronized geschützt wurde).
Du synchronisierst hier die gesamte Schleife, daher dein Problem.
Versuchs mal mit
while(true) {
synchronized(MyLockObject) {
model.addElement("Dumm gelaufen"));
}
}
Dann dürfte das schon etwas anders aussehen.
das hilft damit beide Threads arbeiten können, aber die Exception kann
es dann noch immer geben, weil remove vielleicht for add ausgeführt wird.

Gruss theo
Michael Rauscher
2009-03-02 11:31:42 UTC
Permalink
Post by Ernst Baumann
... nur will ich einfach wissen, warum man die konkurrierenden
Swing-Teile nicht voneinander mit synchronized schützen kann.
Das ist doch ganz einfach: es gibt einen Thread (EDT) von dem aus ohne
Synchronisation auf Komponenten zugegriffen wird. Dann kannst Du in
anderen Threads synchronisieren, was Du willst, es wird nichts helfen.
Post by Ernst Baumann
c)
Bei meinem früheren Postings gibt es keine Endlosschleifen.
Deshalb könnte ich doch "theoretisch" als Alternative (z.B. zu meinem
Programm in meinem Ursprungsposting) ein Programm schreiben, das mit
synchronized arbeitet und also auf die Empfehlung ("Packe also, wie
bereits mehrfach erwähnt, die Zugriffe auf Komponenten einfach in ein
Runnable und ...") verzichten.
Ist das richtig oder wo ist mein Denkfehler?
Du vergisst, dass die Swing-Komponenten selbst unsynchronisiert auf
Felder etc. zugreifen. Daran kann auch Dein o. g. Ansatz nichts ändern.

Wenn Du Saschas Änderungen berücksichtigst, stellst Du zwar sicher, dass
addElement und removeElement nicht parallel ausgeführt werden. Das
ändert aber nichts an der parallelen Ausführung zum EDT.

Du hast es dann mit drei Threads (main-Thread ist hier uninteressant) zu
tun: Add-Thread, Remove-Thread und EDT.

Beispiel (inkl. Sachas Änderungen aber ohne Berücksichtigung der
tatsächlichen Gegebenheiten):

Add-Thread fügt ein Element in das Model ein. Das geschieht
synchronisiert über das MyLockObject, so dass der Remove-Thread warten
muss.

Das Model informiert seine Listener über das neue Element, die Liste
stellt ein repaint-Ereignis in die EventQueue. Damit ist addElement
erledigt, die Sperre wird aufgehoben. Das Model enthält nun z. B. 300
Elemente.

Der EDT arbeitet die EventQueue ab, die Liste wird neu gezeichnet. Beim
Zeichnen wird mit einer Schleife über 300 Elemente des Models iteriert.

Während dessen kann nun der Remove-Thread zum Einsatz kommen, da die
Sperre des Add-Threads ja aufgehoben ist. Er entfernt ein Element, d. h.
im Model sind nur noch 299 Elemente.

Die Schleife kommt nun an Element 300. Dieses gibt es nicht mehr -->
Problem.

Gruß
Michael
Jochen Theodorou
2009-03-02 14:02:09 UTC
Permalink
Ernst Baumann schrieb:
[...]
Post by Ernst Baumann
a)
In "Java ist auch eine Insel" unter
http://openbook.galileocomputing.de/javainsel/java-16.htm#t316
steht: Swing ist nicht threadsicher.
-----------------------------------------------------
import javax.swing.*;
public class SwingNoSyncDemo{
public static void main( String args[] ){
final DefaultListModel model = new DefaultListModel();
JList list = new JList( model );
JFrame frame = new JFrame();
frame.getContentPane().add( list );
frame.setSize( 200, 100 );
frame.show();
new Thread(){ public void run() {
setPriority( Thread.MIN_PRIORITY );
while ( true )
model.addElement( "Dumm gelaufen" );
}
}.start();
new Thread(){ public void run() {
setPriority( Thread.MIN_PRIORITY );
while ( true )
model.removeElement( "Dumm gelaufen" );
}
}.start();
}
}
-----------------------------------------------------
java.lang.ArrayIndexOutOfBoundsException: 4145 >=
4145
Also gibt es Probleme!
wann immer Threads auf gemeinsame Daten zugreifen (mindestens einer
davon schreibend) musst du handeln. Hier ist das model und du hast den
EDT, den remove- und den add-Thread, also 3, die darauf zugreifen.

Wenn du verhindern willst, dass ein remove ausgeführt wird, obwohl noch
Post by Ernst Baumann
class SyncCounter() {
int x
}
[...]
Post by Ernst Baumann
finalSyncCounter counter = new SyncCounter()
new Thread(){ public void run() {
setPriority( Thread.MIN_PRIORITY );
while (true) {
synchronized(counter) {
counter.x++;
model.addElement( "Dumm gelaufen" );
}
}
}
}.start();
new Thread(){ public void run() {
setPriority( Thread.MIN_PRIORITY );
while (true) {
synchronized(counter) {
if (counter.x==0) continue;
counter.x--;
model.removeElement( "Dumm gelaufen" );
}
}
}
}.start();
ist zwar ungetestet und man kann das noch sehr viel schöner lösen, aber
im Prinzip sollte das so funktionieren. Wollte ich zum Beispiel
sicherstellen, dass immer nur ein Element "Dumm gelaufen" existiert,
dann müsste man vor counter.x++ noch ein "if (counter.x!=0) continue;"
setzen.

Allerdings wird das Gesamtproblem hiermit nur zum Teil gelöst, denn wir
habven jetzt zwar die Threads mit addElement und removeElement unter
kontrolle, aber nicht den EDT, der das Modell benutzt um etwas
anzuzeigen. Und dann kann es passieren, dass Swing feststellt ein
element ist da und will es anzeigen, als es das aber versucht war
vielleicht removeElement schon ausgeführt und Swing greift ins Leere...
was möglicherweise eine Exception produzieren wird.
Post by Ernst Baumann
while ( true )
model.addElement( "Dumm gelaufen" );
und
while ( true )
model.removeElement( "Dumm gelaufen" );
durch ein synchronized voneinander schützen.
-----------------------------------------------------
package testsych1;
import javax.swing.*;
public class MainTestSynch1{
public static Object MyLockObjekt = new Object();
public static void main( String args[] ){
final DefaultListModel model = new DefaultListModel();
JList list = new JList( model );
JFrame frame = new JFrame();
frame.getContentPane().add( list );
frame.setSize( 200, 100 );
frame.show();
new Thread(){ public void run() {
setPriority( Thread.MIN_PRIORITY );
synchronized(MyLockObjekt){
while ( true ){
System.out.println("Add-Thread");
model.addElement( "Dumm gelaufen" );
}
}
}
}.start();
new Thread(){ public void run() {
setPriority( Thread.MIN_PRIORITY );
synchronized(MyLockObjekt){
while ( true ){
model.removeElement( "Dumm gelaufen" );
System.out.println("Remove-Thread");
}
}
}
}.start();
}
}
-----------------------------------------------------
Es wird immer nur der Remove-Thread ausgeführt.
theoretisch könnte es auch der add-Thread sein. Laufen kann jedenfalls
nur ein Thread in der Endlosschleifen, weil du mit synchronized ja genau
das sichergestellt hast.

[...]
Post by Ernst Baumann
Man hat das Problem also nur, weil es Endlosschleifen gibt.
Ist das richtig?
du meinst das Problem, dass der eine Thread ausgeführt wird, der andere
aber *nie*, dann ja. Allerdings ist es kein so großer Unterschied für
den Benutzer einer GUI ob der andere Thread jetzt nie drankommt, oder
nur die nächsten Minuten nicht. Das ist dann halt ein "gefühltes nie"
Post by Ernst Baumann
c)
Bei meinem früheren Postings gibt es keine Endlosschleifen.
Deshalb könnte ich doch "theoretisch" als Alternative (z.B. zu meinem
Programm in meinem Ursprungsposting) ein Programm schreiben, das mit
synchronized arbeitet und also auf die Empfehlung ("Packe also, wie
bereits mehrfach erwähnt, die Zugriffe auf Komponenten einfach in ein
Runnable und ...") verzichten.
Ist das richtig oder wo ist mein Denkfehler?
Weil in Swing eben eine Endlosschleife existiert ;) Wie sonst könnte der
EDT auf deine Runnables reagieren?

Gruss theo
Ernst Baumann
2009-03-03 16:25:04 UTC
Permalink
Ich habe das folgende Programm (den Vorschlag von Marcus aufgegriffen)
erstellt und getestet:
---------------------------------------------------------------------------------
package threadproblem1;
import javax.swing.*;

public class MainThreadProblem1 {
public static Object MyLockObjekt = new Object();
public static void main( String args[] ){
final DefaultListModel model = new DefaultListModel();
JList list = new JList( model );
JFrame frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.getContentPane().add( list );
frame.setSize( 200, 100 );
frame.show();

new Thread(){ public void run() {
int i=0;
setPriority( Thread.MIN_PRIORITY );
while ( true ){
synchronized(MyLockObjekt){
System.out.println("Add-Thread "+i);
model.addElement("Add-Thread "+i);
i++;
}
}
}
}.start();

new Thread(){ public void run() {
int i=0;
setPriority( Thread.MIN_PRIORITY );
try{
Thread.sleep(1000);
}
catch(Exception e){}
while ( true ){
synchronized(MyLockObjekt){
System.out.println("Remove-Thread "+i);
model.removeElement( "Remove-Thread "+i);
i++;
}
}
}
}.start();
}
}
---------------------------------------------------------------------------------
Bemerkung:
das Programm ist fehlerhaft, weil u.a. nach der Laufzeit von ca. 30
Sekunden folgende Meldungen auf dem Bildschirm erschien:
...
Remove-Thread 8737
Add-Thread 16323
Exception in thread "AWT-EventQueue-0"
java.lang.ArrayIndexOutOfBoundsException: 16324
at
javax.swing.plaf.basic.BasicListUI.updateLayoutState(BasicListUI.java:1162)
Remove-Thread 8738
at
javax.swing.plaf.basic.BasicListUI.maybeUpdateLayoutState(BasicListUI.java:1105)
at
javax.swing.plaf.basic.BasicListUI.paint(BasicListUI.java:237)
at javax.swing.plaf.ComponentUI.update(ComponentUI.java:142)
at javax.swing.JComponent.paintComponent(JComponent.java:742)
at javax.swing.JComponent.paint(JComponent.java:1005)
at
javax.swing.JComponent.paintWithOffscreenBuffer(JComponent.java:4963)
at
javax.swing.JComponent.paintDoubleBuffered(JComponent.java:4916)
at
javax.swing.JComponent._paintImmediately(JComponent.java:4859)
at
javax.swing.JComponent.paintImmediately(JComponent.java:4666)
at
javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:451)
at
javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(SystemEventQueueUtilities.java:114)
at
java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:461)
at
java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:242)
at
java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:163)
at
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:157)
at
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:149)
at
java.awt.EventDispatchThread.run(EventDispatchThread.java:110)
Add-Thread 16324
Remove-Thread 8739
Add-Thread 16325
....



I) Diese Meldungen kommen, weil auf die gleichen Daten quasiparallel
von verschiedenen Threads "ungeschützt", also ungeregelt zugegriffen
wird.
Ist das richtig?

II)
1)
Man hat 4 Threads:
Main-Thread, EDT, add-Thread und remove-Thread.
Im Main-Thread, add-Thread und remove-Thread. kann man als
Programmierer mit synchronized parallele Zugriffe auf die gleichen
Daten verhindern.

3)
Da aber die Liste (ein JList Objekt) laufend durch model.addElement()
und model.removeElement() verändert wird, muss sich diese Änderung auf
dem Bildschirm irgendwann auswirken.
Dies geschieht dadurch, dass model.addElement() und
model.removeElement() indirekt, d.h. versteckt (implizit) über
repaint() mit paintComponent() Malaufträge in die Warteschlange des
EDT stellen, die dann vom EDT irgendwann abgearbeitet wird. Da man als
Programmierer aber keinen Einfluß darauf hat, wann dieses
paintComponent() aufgerufen wird und erst recht _keinen_ Einfluß
darauf hat, dieses paintComponent() mit synchronized geregelt, d.h.
nichtparallel auf gemeinsame Daten anderer Threads zugreifen zu
lassen, ist es somit unmöglich mit synchroized einen gemeinsamen
Zugriffe der 4 Threads Main-Thread, EDT, add-Thread und
remove-Thread.auf gemeinsame Daten zu regeln.
Da man also mit synchronized keinen Einfluß darauf hat, den Zugriff
auf gemeinsame Daten (Swing-Komponenten) des EDT und eines anderen
Threads zu regeln, kann man dies z.B. dadurch machen, dass man den
Zugriff auf gemeinsame Daten (Swing-Komponenten) dadurch regelt, dass
man diese im EDT veranlasst (weil innerhalb eines Threads, also hier
EDT die Abarbeitung nichtparallel, also sequentiell erfolgt ).
Dies kann z.B. durch die Verwendung von invokeLater() realisiert
werden.
Ist das richtig?

mfg
Ernst
Jochen Theodorou
2009-03-03 19:39:07 UTC
Permalink
Post by Ernst Baumann
Ich habe das folgende Programm (den Vorschlag von Marcus aufgegriffen)
---------------------------------------------------------------------------------
package threadproblem1;
import javax.swing.*;
public class MainThreadProblem1 {
public static Object MyLockObjekt = new Object();
public static void main( String args[] ){
final DefaultListModel model = new DefaultListModel();
JList list = new JList( model );
JFrame frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.getContentPane().add( list );
frame.setSize( 200, 100 );
frame.show();
new Thread(){ public void run() {
int i=0;
setPriority( Thread.MIN_PRIORITY );
while ( true ){
synchronized(MyLockObjekt){
System.out.println("Add-Thread "+i);
model.addElement("Add-Thread "+i);
i++;
}
}
}
}.start();
new Thread(){ public void run() {
int i=0;
setPriority( Thread.MIN_PRIORITY );
try{
Thread.sleep(1000);
}
catch(Exception e){}
while ( true ){
synchronized(MyLockObjekt){
System.out.println("Remove-Thread "+i);
model.removeElement( "Remove-Thread "+i);
i++;
}
}
}
}.start();
}
}
---------------------------------------------------------------------------------
das Programm ist fehlerhaft, weil u.a. nach der Laufzeit von ca. 30
...
Remove-Thread 8737
Add-Thread 16323
Exception in thread "AWT-EventQueue-0"
java.lang.ArrayIndexOutOfBoundsException: 16324
javax.swing.plaf.basic.BasicListUI.updateLayoutState(BasicListUI.java:1162)
das hier heisst er wollte gerade was neu zeichnen...

[...]
Post by Ernst Baumann
java.awt.EventDispatchThread.run(EventDispatchThread.java:110)
und das heisst, dass Zeichnen wurde vom EDT aus ausgeführt... oder
sollte zumindest.

[...]
Post by Ernst Baumann
I) Diese Meldungen kommen, weil auf die gleichen Daten quasiparallel
von verschiedenen Threads "ungeschützt", also ungeregelt zugegriffen
wird.
Ist das richtig?
ja
Post by Ernst Baumann
II)
1)
Main-Thread, EDT, add-Thread und remove-Thread.
Im Main-Thread, add-Thread und remove-Thread. kann man als
Programmierer mit synchronized parallele Zugriffe auf die gleichen
Daten verhindern.
ja
Post by Ernst Baumann
3)
Da aber die Liste (ein JList Objekt) laufend durch model.addElement()
und model.removeElement() verändert wird, muss sich diese Änderung auf
dem Bildschirm irgendwann auswirken.
Dies geschieht dadurch, dass model.addElement() und
model.removeElement() indirekt, d.h. versteckt (implizit) über
repaint() mit paintComponent() Malaufträge in die Warteschlange des
EDT stellen, die dann vom EDT irgendwann abgearbeitet wird.
ich glaube das Modell schickt ein Event and die Oberfläche, die sich als
Listener registriert hat.. oder so ähnlich. Aber das führt natürlich
dann zu einem repaint.
Post by Ernst Baumann
Da man als
Programmierer aber keinen Einfluß darauf hat, wann dieses
paintComponent() aufgerufen wird und erst recht _keinen_ Einfluß
darauf hat, dieses paintComponent() mit synchronized geregelt, d.h.
nichtparallel auf gemeinsame Daten anderer Threads zugreifen zu
lassen, ist es somit unmöglich mit synchroized einen gemeinsamen
Zugriffe der 4 Threads Main-Thread, EDT, add-Thread und
remove-Thread.auf gemeinsame Daten zu regeln.
Dem stimme ich so nicht zu. Man kann paintComponent überschreiben und
dort synchronized benutzen. Das setzt natürlich voraus, dass du eine
eigene Komponente benutzt. Allerdings greift nicht nur paintComponent
auf das Modell zu.

Du könntest auch versuchen ein eigenes Modell zu benutzen, dass nur
synchronisiert Zugriffe erlaubt... allerdings gibt es dort meist eine
Methode, die die Anzahl der Componenten zurück gibt, und eine, die per
Index auf diese zu greift. Da du aber zwischendurch vielleicht wieder
was entfernst soll dann vielliecht trotzdem eine Componente dargestellt
werden, die es schon nicht mehr gibt. Klappt so also nicht wirklich.

Die Variante mit paintComponent würde besser funktionieren, weil
solange4 das läuft ja auch diese 2 Methoden aufgerufen werden und diese
jetzt quasi in einem synchronized-Block laufen. Aber wie geagt, auch
andere teile vonSwing greifen auf das Modell zu glaube ich. Und dann
hast du wieder das Problem.
Post by Ernst Baumann
Da man also mit synchronized keinen Einfluß darauf hat, den Zugriff
auf gemeinsame Daten (Swing-Komponenten) des EDT und eines anderen
Threads zu regeln, kann man dies z.B. dadurch machen, dass man den
Zugriff auf gemeinsame Daten (Swing-Komponenten) dadurch regelt, dass
man diese im EDT veranlasst (weil innerhalb eines Threads, also hier
EDT die Abarbeitung nichtparallel, also sequentiell erfolgt ).
Dies kann z.B. durch die Verwendung von invokeLater() realisiert
werden.
Ist das richtig?
ja

Gruss theo
Ernst Baumann
2009-03-04 16:58:01 UTC
Permalink
Post by Jochen Theodorou
Post by Ernst Baumann
Da man als
Programmierer aber keinen Einfluß darauf hat, wann dieses
paintComponent() aufgerufen wird und erst recht _keinen_ Einfluß
darauf hat, dieses paintComponent() mit synchronized geregelt, d.h.
nichtparallel auf gemeinsame Daten anderer Threads zugreifen zu
lassen, ist es somit unmöglich mit synchroized einen gemeinsamen
Zugriffe der 4 Threads Main-Thread, EDT, add-Thread und
remove-Thread.auf gemeinsame Daten zu regeln.
Dem stimme ich so nicht zu. Man kann paintComponent überschreiben und
dort synchronized benutzen. Das setzt natürlich voraus, dass du eine
eigene Komponente benutzt.
Meinst du so wie ich es in meinem Programm (mit meiner eigenen
Komponente MyZeichenflaeche) unten gemacht habe?
Post by Jochen Theodorou
Allerdings greift nicht nur paintComponent
auf das Modell zu.
Du könntest auch versuchen ein eigenes Modell zu benutzen, dass nur
synchronisiert Zugriffe erlaubt... allerdings gibt es dort meist eine
Methode, die die Anzahl der Componenten zurück gibt, und eine, die per
Index auf diese zu greift. Da du aber zwischendurch vielleicht wieder
was entfernst soll dann vielliecht trotzdem eine Componente dargestellt
werden, die es schon nicht mehr gibt. Klappt so also nicht wirklich.
Meinst du mit "eigenes Modell" eine selbst programmierte Klasse, oder
was meinst du damit?
Bei einer selbst programmierten Klasse könnte ich doch alles so
einrichten, dass es funktioniert, oder?
Post by Jochen Theodorou
Die Variante mit paintComponent
du meinst so etwas, wie unten in meinem Programm?
Post by Jochen Theodorou
würde besser funktionieren, weil
solange4 das läuft ja auch diese 2 Methoden
du meinst dem add-Thread und den remove-Thread?
Post by Jochen Theodorou
aufgerufen werden und diese
jetzt quasi in einem synchronized-Block laufen. Aber wie geagt, auch
andere teile vonSwing greifen auf das Modell zu glaube ich. Und dann
hast du wieder das Problem.
Du meinst folgendes:
Es kann noch andere Methoden wie paintComponent() geben, über die
Swing außerdem sonst noch auf die gemeinsamen Daten von add-Thread,
remove-Thread, main-Thread zugreift.
Erst, wenn man diese alle kennt, kann man mit diesen analog so
verfahren wie hier (in meinem Programm unten) mit paintComponent()).
Dann wäre das Problem der Vermeidung der quasiparallelen Zugriffe auf
gemeinsame Daten verschiedener Threads mit Hilfe von synchronized
gelöst.
Stimmt das ?

mfg
Ernst




Beschreibung des gleich folgenden Programms:
Hier wird in einer eigenen Zeichenfäche MyZeichenflaeche die Liste
JList angebracht (und mit ihr die Listeneinträge). Damit kann
paintComponent() überschrieben werden. Dies geschieht dadurch, dass
man die Methode der Oberklasse, also des paintComponent() von Swing
mit synchronized schützt, so dass der quasiparallele Zugriff der
Threads EDT, add-Thread, remove-Thread nicht mehr möglich ist.

-----------------------------------------------------------------
package threadproblem2;

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

public class MainThreadProblem2 {
public static Object MyLockObjekt = new Object();
public static void main( String args[] ){
//final DefaultListModel model = new DefaultListModel();
//JList list = new JList(model);
JFrame frame = new JFrame();
final MyZeichenflaeche myZ = new MyZeichenflaeche();

frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
//frame.getContentPane().add( list );
frame.setSize( 200, 100 );
frame.show();

new Thread(){ public void run() {
int i=0;
setPriority( Thread.MIN_PRIORITY );
while ( true ){
synchronized(MyLockObjekt){
System.out.println("Add-Thread "+i);
myZ.addiereElement(i);
//model.addElement("Add-Thread "+i);
i++;
}
}
}
}.start();


new Thread(){ public void run() {
int i=0;
setPriority( Thread.MIN_PRIORITY );
try{
Thread.sleep(1000);
}
catch(Exception e){}
while ( true ){
synchronized(MyLockObjekt){
System.out.println("Remove-Thread "+i);
myZ.removeElement(i);
i++;
}
}
}
}.start();
}
}



class MyZeichenflaeche extends JPanel{
public Object MyLockObjekt = new Object();
private DefaultListModel model = new DefaultListModel();
JList list = new JList(model);

public void addiereElement(int index){
model.addElement("Add-Thread "+index);
}

public void removeElement(int index){
model.removeElement("Remove-Thread "+index);
}


public void paintComponent(Graphics g){
synchronized(MyLockObjekt){
super.paint(g);
}
}
}
-----------------------------------------------------------------
Jochen Theodorou
2009-03-05 13:44:57 UTC
Permalink
Post by Ernst Baumann
Post by Jochen Theodorou
Post by Ernst Baumann
Da man als
Programmierer aber keinen Einfluß darauf hat, wann dieses
paintComponent() aufgerufen wird und erst recht _keinen_ Einfluß
darauf hat, dieses paintComponent() mit synchronized geregelt, d.h.
nichtparallel auf gemeinsame Daten anderer Threads zugreifen zu
lassen, ist es somit unmöglich mit synchroized einen gemeinsamen
Zugriffe der 4 Threads Main-Thread, EDT, add-Thread und
remove-Thread.auf gemeinsame Daten zu regeln.
Dem stimme ich so nicht zu. Man kann paintComponent überschreiben und
dort synchronized benutzen. Das setzt natürlich voraus, dass du eine
eigene Komponente benutzt.
Meinst du so wie ich es in meinem Programm (mit meiner eigenen
Komponente MyZeichenflaeche) unten gemacht habe?
fast es bringt nichts wenn Adder- und Remover-Thread einen lock
benutzen, aber MyZeichenflaeche einen anderen.. das muss schon das
identische Objekt sein.
Post by Ernst Baumann
Post by Jochen Theodorou
Allerdings greift nicht nur paintComponent
auf das Modell zu.
Du könntest auch versuchen ein eigenes Modell zu benutzen, dass nur
synchronisiert Zugriffe erlaubt... allerdings gibt es dort meist eine
Methode, die die Anzahl der Componenten zurück gibt, und eine, die per
Index auf diese zu greift. Da du aber zwischendurch vielleicht wieder
was entfernst soll dann vielliecht trotzdem eine Componente dargestellt
werden, die es schon nicht mehr gibt. Klappt so also nicht wirklich.
Meinst du mit "eigenes Modell" eine selbst programmierte Klasse, oder
was meinst du damit?
in dem Fall eine Subklasse von ListModel bzw. DefaultListModel
Post by Ernst Baumann
Bei einer selbst programmierten Klasse könnte ich doch alles so
einrichten, dass es funktioniert, oder?
ja und nein. Stell dir mal vor die GUI will die JList zeichnen und ruft
dafür erstmal getSize() auf und das gibt zum Beipsiel 10 zurück. Und als
nächstes will die GUI mittels getElementAt(9) das letzte Element holen
um es zeichnen zu können. Jetzt stell dir vor, dass in der Zeit zwischen
dem getSize() und dem geElementAt(9) der Remover-Thread das Element
enternt. Das heisst es fehlt dir hier die Kontrolle. Um sicher zu
stellen, dass das getSize() zu dem getElementAt(9) passt, weil ein
anderer Thread (der EDT) das macht.
Post by Ernst Baumann
Post by Jochen Theodorou
Die Variante mit paintComponent
du meinst so etwas, wie unten in meinem Programm?
ja, aber wie gesagt gibt das nicht wirklich Sicherheit, denn wenn ein
Element in die JList eingefügt wird, dann wird ein ListDataEvent
ausgelöst und es dieses hat einen index. Wenn jetzt das Element mit
diesem Index entfernt wurde bevor jemand darauf zugreift dann gibt es
wieder ein Problem
Post by Ernst Baumann
Post by Jochen Theodorou
würde besser funktionieren, weil
solange das läuft ja auch diese 2 Methoden
du meinst dem add-Thread und den remove-Thread?
nein, getSize() und getElementAt(int)
Post by Ernst Baumann
Post by Jochen Theodorou
aufgerufen werden und diese
jetzt quasi in einem synchronized-Block laufen. Aber wie geagt, auch
andere Teile von Swing greifen auf das Modell zu glaube ich. Und dann
hast du wieder das Problem.
Es kann noch andere Methoden wie paintComponent() geben, über die
Swing außerdem sonst noch auf die gemeinsamen Daten von add-Thread,
remove-Thread, main-Thread zugreift.
ja
Post by Ernst Baumann
Erst, wenn man diese alle kennt, kann man mit diesen analog so
verfahren wie hier (in meinem Programm unten) mit paintComponent()).
sofern du das beeinflussen kannst, ja
Post by Ernst Baumann
Dann wäre das Problem der Vermeidung der quasiparallelen Zugriffe auf
gemeinsame Daten verschiedener Threads mit Hilfe von synchronized
gelöst.
Stimmt das ?
ja

Gruss theo
Ernst Baumann
2009-03-06 09:56:44 UTC
Permalink
1)
Post by Jochen Theodorou
Post by Ernst Baumann
Meinst du so wie ich es in meinem Programm (mit meiner eigenen
Komponente MyZeichenflaeche) unten gemacht habe?
fast es bringt nichts wenn Adder- und Remover-Thread einen lock
benutzen, aber MyZeichenflaeche einen anderen.. das muss schon das
identische Objekt sein.
Du hast recht, ich hatte einen Denkfehler!
Ist das koorigierte Programm richtig (siehe unten)?
2)
Post by Jochen Theodorou
Post by Ernst Baumann
Meinst du mit "eigenes Modell" eine selbst programmierte Klasse, oder
was meinst du damit?
in dem Fall eine Subklasse von ListModel bzw. DefaultListModel
Post by Ernst Baumann
Bei einer selbst programmierten Klasse könnte ich doch alles so
einrichten, dass es funktioniert, oder?
ja und nein. Stell dir mal vor die GUI will die JList zeichnen und ruft
dafür erstmal getSize() auf und das gibt zum Beipsiel 10 zurück. Und als
nächstes will die GUI mittels getElementAt(9) das letzte Element holen
um es zeichnen zu können. Jetzt stell dir vor, dass in der Zeit zwischen
dem getSize() und dem geElementAt(9) der Remover-Thread das Element
enternt. Das heisst es fehlt dir hier die Kontrolle. Um sicher zu
stellen, dass das getSize() zu dem getElementAt(9) passt, weil ein
anderer Thread (der EDT) das macht.
Man müsste eben noch _alle_andere Swing-Methoden (ich nennen sie hier
RSM als Abkürzung für restliche Swing-Methoden) außer paintComponent()
kennen, die sonst noch auf die gemeinsamen Daten von add-Thread,
remove-Thread, main-Thread zugreifen.
Ist das richtig?

3)
Post by Jochen Theodorou
Post by Ernst Baumann
Post by Jochen Theodorou
Die Variante mit paintComponent
du meinst so etwas, wie unten in meinem Programm?
ja, aber wie gesagt gibt das nicht wirklich Sicherheit, denn wenn ein
Element in die JList eingefügt wird, dann wird ein ListDataEvent
ausgelöst und es dieses hat einen index. Wenn jetzt das Element mit
diesem Index entfernt wurde bevor jemand darauf zugreift dann gibt es
wieder ein Problem
Da in meinem Programm unten jetzt getSize() und geElementAt(9)
in einem - wie du schreibst - "quasi synchronized-Block laufen",
(weil getSize() und geElementAt(9) - davon geh ich mal aus - intern in
der von mir durch synchronized geschützten Methode
super.paintComponent(...) vorkommen) kann es von dieser Seite aus
keine Probleme geben.
Mit "wieder ein Problem" meinst du die anderen Swing-Methoden RSM
Ist das richtig?

4)
Frage:
Warum werden keine Einträge in meinem Programm auf dem Bildschirm
angezeigt?

5)
Mein neues Programm wäre theoretisch fehlerfrei (bzgl.
Datenkonsistenz), wenn ich alle anderen Swing-Methoden RSM kennen
würde, die sonst noch auf die gemeinsamen Daten von add-Thread,
remove-Thread, main-Thread zugreifen und ich dann mit diesen RSM so
verfahre (überschreibe), wie mit paintComponent(...).
Dann wäre das Problem der Vermeidung der quasiparallelen Zugriffe auf
gemeinsame Daten verschiedener Threads mit Hilfe von synchronized
gelöst.
Stimmt das ?


mfg
Ernst


-------------------------------------------------------------------------
package threadproblem3;

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

public class MainThreadProblem3 {
public static Object MyLockObjekt = new Object();
public static void main( String args[] ){
JFrame frame = new JFrame();
final MyZeichenflaeche myZ = new MyZeichenflaeche(MyLockObjekt);

frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setSize( 200, 100 );
frame.show();

new Thread(){ public void run() {
int i=0;
setPriority( Thread.MIN_PRIORITY );
while ( true ){
synchronized(MyLockObjekt){
System.out.println("Add-Thread "+i);
myZ.addiereElement(i);
//model.addElement("Add-Thread "+i);
i++;
}
}
}
}.start();


new Thread(){ public void run() {
int i=0;
setPriority( Thread.MIN_PRIORITY );
try{
Thread.sleep(1000);
}
catch(Exception e){}
while ( true ){
synchronized(MyLockObjekt){
System.out.println("Remove-Thread "+i);
myZ.removeElement(i);
i++;
}
}
}
}.start();
}
}

class MyZeichenflaeche extends JPanel{
public Object MyLockObjekt;
private DefaultListModel model = new DefaultListModel();
JList list = new JList(model);

public MyZeichenflaeche(Object pMyLockObjekt){
MyLockObjekt=pMyLockObjekt;
}

public void addiereElement(int index){
model.addElement("Add-Thread "+index);
}

public void removeElement(int index){
model.removeElement("Remove-Thread "+index);
}


public void paintComponent(Graphics g){
synchronized(MyLockObjekt){
super.paint(g);
}
}
}

-------------------------------------------------------------------------
Michael Rauscher
2009-03-06 10:30:56 UTC
Permalink
Hallo Ernst,
Post by Ernst Baumann
Man müsste eben noch _alle_andere Swing-Methoden (ich nennen sie hier
RSM als Abkürzung für restliche Swing-Methoden) außer paintComponent()
kennen, die sonst noch auf die gemeinsamen Daten von add-Thread,
remove-Thread, main-Thread zugreifen.
Ist das richtig?
Dürfte man erfahren, warum Du Dich zwanghaft an der Lösung eines
Problems versuchst, das in der Praxis keines ist?
Post by Ernst Baumann
Da in meinem Programm unten jetzt getSize() und geElementAt(9)
in einem - wie du schreibst - "quasi synchronized-Block laufen",
(weil getSize() und geElementAt(9) - davon geh ich mal aus - intern in
der von mir durch synchronized geschützten Methode
super.paintComponent(...) vorkommen) kann es von dieser Seite aus
keine Probleme geben.
1. Wird in Deinem Programm überhaupt keine Methode von JList
verwendet, das JList zwar als Objekt aber nicht in der
Komponentenhierarchie existiert, was übrigens gleich die
Antwort auf Deine Frage 4) ist.

2. Ist Deine Annahme bzgl. paintComponent in Deinem Programm auch
dann falsch, wenn die JList dem JPanel hinzugefügt worden wäre.
Du müsstest schon paint überschreiben.

Gruß
Michael
Ernst Baumann
2009-03-06 19:37:21 UTC
Permalink
Post by Michael Rauscher
Post by Ernst Baumann
Man müsste eben noch _alle_andere Swing-Methoden (ich nennen sie hier
RSM als Abkürzung für restliche Swing-Methoden) außer paintComponent()
kennen, die sonst noch auf die gemeinsamen Daten von add-Thread,
remove-Thread, main-Thread zugreifen.
Ist das richtig?
Dürfte man erfahren, warum Du Dich zwanghaft an der Lösung eines
Problems versuchst, das in der Praxis keines ist?
Ich will nachprüfen, ob meine Gedanken richtig sind.
Dank deiner Ausführungen habe ich leider festgestellt, dass sich in
meinen Gedanken ein Denkfehler befindet.
Post by Michael Rauscher
Post by Ernst Baumann
Da in meinem Programm unten jetzt getSize() und geElementAt(9)
in einem - wie du schreibst - "quasi synchronized-Block laufen",
(weil getSize() und geElementAt(9) - davon geh ich mal aus - intern in
der von mir durch synchronized geschützten Methode
super.paintComponent(...) vorkommen) kann es von dieser Seite aus
keine Probleme geben.
1. Wird in Deinem Programm überhaupt keine Methode von JList
verwendet, das JList zwar als Objekt aber nicht in der
Komponentenhierarchie existiert, was übrigens gleich die
Antwort auf Deine Frage 4) ist.
Leider verstehe ich nicht, was du meinst:
Ich habe folgendes gedacht:
Letztendlich wird durch einen irgendwie gearteten Malauftrag in der
EDT-Warteschlange der Standard paintComponent(g) von Swing aufgerufen.
(das ist das paintComponent(g), das aufgerufen wird, wenn ich es
_nicht_ überschreibe). Dieses Standard-Swing paintComponent(g) bringt
ja auch die Einträge von JList auf den Bildschrim, wobei ich eben hier
noch das Problem mit den konkurrierenden remove und add Zugriffen auf
gemeinsame Daten habe.
Also habe ich dieses Standard-Swing paintComponent(g) einfach dadurch
aufgerufen, dass ich paintComponent(g) dadurch überschreibe, indem ich
in meinem paintComponent(g) das Standard-Swing paintComponent(g)
aufrufe und zwar mit super.paintComponent(g).
Jetzt gibt es also die 3 geschützte Threads remove-Thread, add-Thread
und super.paintComponent(g), die keine Dateninkonsistenz erzeugen
dürften:
remove-Thread erzeugt keine Dateninkonsistenz
add-Thread erzeugt keine Dateninkonsistenz
super.paintComponent(g) erzeugt keine Dateninkonsistenz, weil er ja
auch durch synchronized geschützt wird.
Wer soll also die Dateninkonsistenz noch erzeugen?
Wo ist mein Denkfehler?
Post by Michael Rauscher
2. Ist Deine Annahme bzgl. paintComponent in Deinem Programm auch
dann falsch, wenn die JList dem JPanel hinzugefügt worden wäre.
Du müsstest schon paint überschreiben.
Warum?
Das verstehe ich leider auch nicht.
Kannst du mir das ausführlicher erklären?

mfg
Ernst
Michael Rauscher
2009-03-07 14:14:58 UTC
Permalink
Post by Ernst Baumann
Post by Michael Rauscher
Dürfte man erfahren, warum Du Dich zwanghaft an der Lösung eines
Problems versuchst, das in der Praxis keines ist?
Ich will nachprüfen, ob meine Gedanken richtig sind.
Dann muss ich neu formulieren: wieso machst Du Dir Gedanken über Dinge,
die letztlich keine Rolle spielen? :)
Post by Ernst Baumann
Post by Michael Rauscher
1. Wird in Deinem Programm überhaupt keine Methode von JList
verwendet, das JList zwar als Objekt aber nicht in der
Komponentenhierarchie existiert, was übrigens gleich die
Antwort auf Deine Frage 4) ist.
class MyZeichenflaeche extends JPanel{
public Object MyLockObjekt;
private DefaultListModel model = new DefaultListModel();
JList list = new JList(model);
public MyZeichenflaeche(Object pMyLockObjekt){
MyLockObjekt=pMyLockObjekt;
}
Es ist eine JList als Objekt vorhanden, auf welches mittels list
referenziert wird. Nun fehlt aber ein add(list), so dass das
JList-Objekt nicht Teil der Komponentenhierarchie ist und somit auch
nicht angezeigt wird.
Post by Ernst Baumann
Letztendlich wird durch einen irgendwie gearteten Malauftrag in der
EDT-Warteschlange der Standard paintComponent(g) von Swing aufgerufen.
(das ist das paintComponent(g), das aufgerufen wird, wenn ich es
_nicht_ überschreibe). Dieses Standard-Swing paintComponent(g) bringt
ja auch die Einträge von JList auf den Bildschrim, wobei ich eben hier
noch das Problem mit den konkurrierenden remove und add Zugriffen auf
gemeinsame Daten habe.
Salopp könnte man das so formulieren. ABER: nicht das paintComponent des
Panels zeigt die Liste oder gar deren Einträge an. Wenn überhaupt, dann
ist wäre es das paintComponent der Liste (tatsächlich sind es UI-Delegates).
Post by Ernst Baumann
Also habe ich dieses Standard-Swing paintComponent(g) einfach dadurch
aufgerufen, dass ich paintComponent(g) dadurch überschreibe, indem ich
in meinem paintComponent(g) das Standard-Swing paintComponent(g)
aufrufe und zwar mit super.paintComponent(g).
Du überschreibst aber JPanel#paintComponent.
Post by Ernst Baumann
Post by Michael Rauscher
2. Ist Deine Annahme bzgl. paintComponent in Deinem Programm auch
dann falsch, wenn die JList dem JPanel hinzugefügt worden wäre.
Du müsstest schon paint überschreiben.
Warum?
Das verstehe ich leider auch nicht.
Kannst du mir das ausführlicher erklären?
Die Kurzvariante (dient nur der Vorstellung - tatsächlich ist es viel
komplizierter):

Muss eine Komponente (java.awt.Component) neu gezeichnet werden, wird
paint(g) aufgerufen. Bei Container (extends Component) wird dann der
"Malauftrag" weiter an die Elemente des Containers geleitet
(Component#paint). JComponent (extends Container) ruft zusätzlich noch
paintComponent auf.

Mit anderen Worten: es bringt Dir nichts, wenn Du JPanel#paintComponent
überschreibst, tatsächlich aber JList#paintComponent überschreiben willst.

Gruß
Michael
Ernst Baumann
2009-03-08 11:44:23 UTC
Permalink
Post by Michael Rauscher
Dann muss ich neu formulieren: wieso machst Du Dir Gedanken über Dinge,
die letztlich keine Rolle spielen? :)
Es spielt für das Programmieren (vielleicht nicht an dieser Stelle),
aber für ein zukünftiges Programm schon eine Rolle, ob meine Gedanke
(zur Vererbung, usw.) richtig sind oder nicht.
Deswegen Dank an alle für eure mir widersprechenden Beiträge (diese
entsprechen einem Stellglied im Regelkreis)
Post by Michael Rauscher
Post by Ernst Baumann
class MyZeichenflaeche extends JPanel{
public Object MyLockObjekt;
private DefaultListModel model = new DefaultListModel();
JList list = new JList(model);
public MyZeichenflaeche(Object pMyLockObjekt){
MyLockObjekt=pMyLockObjekt;
}
Es ist eine JList als Objekt vorhanden, auf welches mittels list
referenziert wird. Nun fehlt aber ein add(list), so dass das
JList-Objekt nicht Teil der Komponentenhierarchie ist und somit auch
nicht angezeigt wird.
Du hast recht. Ich korrigire mich und sehe auch ein, dass diese
Korrektur auch nichts bringt!

public MyZeichenflaeche(Object pMyLockObjekt){
MyLockObjekt=pMyLockObjekt;
add(list);
}
Post by Michael Rauscher
Post by Ernst Baumann
Letztendlich wird durch einen irgendwie gearteten Malauftrag in der
EDT-Warteschlange der Standard paintComponent(g) von Swing aufgerufen.
(das ist das paintComponent(g), das aufgerufen wird, wenn ich es
_nicht_ überschreibe). Dieses Standard-Swing paintComponent(g) bringt
ja auch die Einträge von JList auf den Bildschrim, wobei ich eben hier
noch das Problem mit den konkurrierenden remove und add Zugriffen auf
gemeinsame Daten habe.
Salopp könnte man das so formulieren. ABER: nicht das paintComponent des
Panels zeigt die Liste oder gar deren Einträge an. Wenn überhaupt, dann
ist wäre es das paintComponent der Liste (tatsächlich sind es UI-Delegates).
Stimmt. Das war mein Denkfehler!
(nur nebenbei:
Was zeigt dann JPanel#paintComponent an?
}
Post by Michael Rauscher
Post by Ernst Baumann
Also habe ich dieses Standard-Swing paintComponent(g) einfach dadurch
aufgerufen, dass ich paintComponent(g) dadurch überschreibe, indem ich
in meinem paintComponent(g) das Standard-Swing paintComponent(g)
aufrufe und zwar mit super.paintComponent(g).
Du überschreibst aber JPanel#paintComponent.
Post by Ernst Baumann
Post by Michael Rauscher
2. Ist Deine Annahme bzgl. paintComponent in Deinem Programm auch
dann falsch, wenn die JList dem JPanel hinzugefügt worden wäre.
Du müsstest schon paint überschreiben.
Warum?
Das verstehe ich leider auch nicht.
Kannst du mir das ausführlicher erklären?
Die Kurzvariante (dient nur der Vorstellung - tatsächlich ist es viel
Muss eine Komponente (java.awt.Component) neu gezeichnet werden, wird
paint(g) aufgerufen. Bei Container (extends Component) wird dann der
"Malauftrag" weiter an die Elemente des Containers geleitet
(Component#paint). JComponent (extends Container) ruft zusätzlich noch
paintComponent auf.
Mit anderen Worten: es bringt Dir nichts, wenn Du JPanel#paintComponent
überschreibst, tatsächlich aber JList#paintComponent überschreiben willst.
Ich habe also das falsche paint überschrieben!

Ist aber der folgende Gedanke von mir, rein theoretisch gesehen,
richtig?
Mein neues, noch zu schreibendes, Programm wäre theoretisch fehlerfrei
(bzgl.Datenkonsistenz), wenn ich alle anderen Swing-Methoden (ich
nenne sie RSM) kennen würde, die sonst noch auf die gemeinsamen Daten
von add-Thread, remove-Thread, main-Thread zugreifen und ich dann mit
diesen RSM so verfahre (überschreibe), wie mit paintComponent(...)
(also super.... mit synchronized schützen).
Dann wäre das Problem der Vermeidung der quasiparallelen Zugriffe auf
gemeinsame Daten verschiedener Threads mit Hilfe von synchronized
gelöst.
Stimmt das ?


mfg
Ernst
Michael Rauscher
2009-03-08 17:09:17 UTC
Permalink
Post by Ernst Baumann
Was zeigt dann JPanel#paintComponent an?
Das ist zwar grundsätzlich vom LaF abhängig aber das UI-Delegate wird
den Hintergrund mit der Hintergrundfarbe füllen, sofern
JPanel#isOpaque() == true liefert.
Post by Ernst Baumann
Post by Michael Rauscher
Mit anderen Worten: es bringt Dir nichts, wenn Du JPanel#paintComponent
überschreibst, tatsächlich aber JList#paintComponent überschreiben willst.
Ich habe also das falsche paint überschrieben!
Das falsche paintComponent, ja.
Post by Ernst Baumann
Ist aber der folgende Gedanke von mir, rein theoretisch gesehen,
richtig?
Mein neues, noch zu schreibendes, Programm wäre theoretisch fehlerfrei
(bzgl.Datenkonsistenz), wenn ich alle anderen Swing-Methoden (ich
nenne sie RSM) kennen würde, die sonst noch auf die gemeinsamen Daten
von add-Thread, remove-Thread, main-Thread zugreifen und ich dann mit
diesen RSM so verfahre (überschreibe), wie mit paintComponent(...)
(also super.... mit synchronized schützen).
Dann wäre das Problem der Vermeidung der quasiparallelen Zugriffe auf
gemeinsame Daten verschiedener Threads mit Hilfe von synchronized
gelöst.
Stimmt das ?
Wenn alle Threads ausschließlich synchronisiert auf gemeinsame Daten
zugreifen, entspricht dies der sequentiellen Ausführung in einem Thread
und dürfte somit dem Einstellen in die EventQueue mit Abarbeitung durch
den EDT gleichzusetzen sein.

Gruß
Michael
Ernst Baumann
2009-03-10 19:27:37 UTC
Permalink
Vielen Dank für eure wertvollen Beiträge.
Da ich jetzt glaube, die Problematik einigermassen verstanden zu
haben, beede ich von meiner Seite die Diskussion.
(Habe alles für mich zusammengefasst)

mfg
Ernst

Ernst Baumann
2009-03-03 16:25:30 UTC
Permalink
Einige Fragen (was nicht synchronized betrifft) in meinem vorletzten
Posting sind noch offen.
Könntest du diese noch beantworten, damit ich das zu "meinen Akten"
nehmen kann?

mfg
Ernst
Jochen Theodorou
2009-03-03 19:39:33 UTC
Permalink
Post by Ernst Baumann
Einige Fragen (was nicht synchronized betrifft) in meinem vorletzten
Posting sind noch offen.
Könntest du diese noch beantworten, damit ich das zu "meinen Akten"
nehmen kann?
welche denn?

Gruss theo
Ernst Baumann
2009-03-04 16:58:15 UTC
Permalink
Post by Jochen Theodorou
Post by Ernst Baumann
Einige Fragen (was nicht synchronized betrifft) in meinem vorletzten
Posting sind noch offen.
Könntest du diese noch beantworten, damit ich das zu "meinen Akten"
nehmen kann?
welche denn?
Diese hier:


1)
Post by Jochen Theodorou
na das würde ja schon mal erklären warum Uwischenbilder verloren gehen
können... Wenn du direkt zeichnest, dann wird das ja auf eine Fläche
gezeichnet, die durch repaint vorbereitet wurde. Folglich bleibt der
Malvorgang aus, wenn das repaint nicht durchgeführt wird. Im Original
zeichnest ja in ein Bild und das Zeichnen wir von deinem runnable durch
.male() ausgelöst. male() wird aber jedesmal ausgeführt, wärend
paintComponentvielleicht ausbleibt.
Stimmt, so weit hatte ich nicht gedacht!
Was passiert bei meinem Programm im Ursprungsposting, wenn ein
paintComponentvielleicht ausbleibt?
Dann befindet sich immer noch das Bild vom letzten paintComponent
(z.B.Kreise 1-3) auf dem Bildschirm und dieses Bild ist korrekt, weil
in das Bild myimg bei jedem Schleifendurchgang der neue Kreis
hinzugezeichnet wird.
Wenn von nun ab aber dann das paintComponent z.B. genau 2 Mal
hintereinander ausfällt, wird 2 Mal noch das alte Bild (Kreise 1-3)
auf dem Bildschirm gebracht. D.h. 2 Mal sieht man nicht die aktuellen
Bilder Kreise 1-4 und dann Kreis 1-5. Erst danach sieht man wieder die
aktuellen Bild Kreise 1-5, Kreise 1-6, (Kreise 1-7, usw.
Also: _nur_ während dieser kurzen Zeitspanne sieht man nicht die
aktuellen Bilder, sonst wird alles korrekt dargestellt.
_Weil_ diese Zeitspanne so kurz ist (weil die Verzögerung in sleep so
klein gewählt wurde) entdeckt dies der Anwender nicht.
Hätte er sehr, sehr gute Augen, wäre ihm das aufgefallen.
Bei meinem Programm im letzten Posting fällt das dagegen jedem
Anwender auf, weil die verlorenen Kreise bei einem neuen repaint nicht
wieder gezeichnet werden.
Ist das richtig?
2)
Post by Jochen Theodorou
Post by Ernst Baumann
Bei einer Veränderung des Fensters (z.B. durch den Anwender, der das
Fenster verschiebt) wird der Zwischenbuffer automatisch (ohne Zutun
des Programmierers) ungültig, also gelöscht.
ob der wirklich gelöscht wird kann ich nicht so genau sagen, aber wenn
ein repaint dadurch notwendig wird, dann ist er zumindest ungülig, ja.
Gibt es außer einer Veränderung des Fensters (z.B. durch den Anwender,
der das Fenster verschiebt) noch eine Möglichkeit, wo der
Zwischenbuffer ungültig wird?
Post by Jochen Theodorou
Post by Ernst Baumann
Deswegen ist es besser, ein Image zu benutzen (mit createImage)
Ist das richtig beschrieben?
Das kommt auf den konkreten Fall an. Wenn du einfach nur den momentanen
Zustand zeichnen willst, dann braucht man das nicht,
Stimmt, dann ist es egal ob in der Vergangenheit Kreise verloren
gegangen sind und ob bei einer Veränderung des Fensters (z.B. durch
den Anwender, der das Fenster verschiebt) Kreise verloren gingen
Ist das richtig?

mfg
Ernst
Jochen Theodorou
2009-03-05 13:51:29 UTC
Permalink
Ernst Baumann schrieb:
[...]
Post by Ernst Baumann
Was passiert bei meinem Programm im Ursprungsposting, wenn ein
paintComponent vielleicht ausbleibt?
Dann befindet sich immer noch das Bild vom letzten paintComponent
(z.B.Kreise 1-3) auf dem Bildschirm und dieses Bild ist korrekt, weil
in das Bild myimg bei jedem Schleifendurchgang der neue Kreis
hinzugezeichnet wird.
ja
Post by Ernst Baumann
Wenn von nun ab aber dann das paintComponent z.B. genau 2 Mal
hintereinander ausfällt, wird 2 Mal noch das alte Bild (Kreise 1-3)
auf dem Bildschirm gebracht. D.h. 2 Mal sieht man nicht die aktuellen
Bilder Kreise 1-4 und dann Kreis 1-5. Erst danach sieht man wieder die
aktuellen Bild Kreise 1-5, Kreise 1-6, (Kreise 1-7, usw.
Also: _nur_ während dieser kurzen Zeitspanne sieht man nicht die
aktuellen Bilder, sonst wird alles korrekt dargestellt.
_Weil_ diese Zeitspanne so kurz ist (weil die Verzögerung in sleep so
klein gewählt wurde) entdeckt dies der Anwender nicht.
naja, es kann ein Eindruck des Ruckelns entstehen, aber das
zwischenzeitlich was nicht gezeichnet wurde sollte der Anwender nicht
bemerken
Post by Ernst Baumann
Hätte er sehr, sehr gute Augen, wäre ihm das aufgefallen.
oder wenn du das sleep zwischen den Zeichenoperationen entsprechend groß
wählst ;)
Post by Ernst Baumann
Bei meinem Programm im letzten Posting fällt das dagegen jedem
Anwender auf, weil die verlorenen Kreise bei einem neuen repaint nicht
wieder gezeichnet werden.
Ist das richtig?
ja
Post by Ernst Baumann
2)
Post by Jochen Theodorou
Post by Ernst Baumann
Bei einer Veränderung des Fensters (z.B. durch den Anwender, der das
Fenster verschiebt) wird der Zwischenbuffer automatisch (ohne Zutun
des Programmierers) ungültig, also gelöscht.
ob der wirklich gelöscht wird kann ich nicht so genau sagen, aber wenn
ein repaint dadurch notwendig wird, dann ist er zumindest ungülig, ja.
Gibt es außer einer Veränderung des Fensters (z.B. durch den Anwender,
der das Fenster verschiebt) noch eine Möglichkeit, wo der
Zwischenbuffer ungültig wird?
vielleicht wenn das Fenster verdeckt wurde... genau weiss ich das nicht,
da man üblicherweise einfach neu zeichnet (komplett oder Ausschnitt)...
und man versucht nicht auf schon gezeichneten Daten aufzubauen. Swing
kann auch mit DirectX oder OpenGl zeichnen und da können auch noch ganz
andere Dinge ein neuzeichnen erzwingen.
Post by Ernst Baumann
Post by Jochen Theodorou
Post by Ernst Baumann
Deswegen ist es besser, ein Image zu benutzen (mit createImage)
Ist das richtig beschrieben?
Das kommt auf den konkreten Fall an. Wenn du einfach nur den momentanen
Zustand zeichnen willst, dann braucht man das nicht,
Stimmt, dann ist es egal ob in der Vergangenheit Kreise verloren
gegangen sind und ob bei einer Veränderung des Fensters (z.B. durch
den Anwender, der das Fenster verschiebt) Kreise verloren gingen
Ist das richtig?
ja

Gruss theo
Marcus Woletz
2009-02-26 21:49:02 UTC
Permalink
Ernst Baumann schrieb:
[...]
Post by Ernst Baumann
2)
a)
Von außerhalb des EDT soll nicht auf bereits realisierte
Swing/AWT-Objekte zugegriffen werden.
Korrekt.
Post by Ernst Baumann
Das wird doch aber bei meinem Programm im Main Thread gemacht, z.B.
diagramm
f
Wenn Du ein klein wenig Glück hast, funktionieren diese Zugriffe in
main(). Das ist jedoch ein wenig Glückssache, und deshalb solltest Du
diese Aufrufe in main() in ein Runnable packen. Steht auch so im Java
Tutorial.
Post by Ernst Baumann
b)
Man darf eigentlich gar nicht von außerhalb, also von einem anderen
Thread auf den EDT zugreifen
Der EDT ist keine Datenstruktur, sondern ein Objekt, das ausgeführt
wird. Deshalb verstehe ich die Frage so nicht ganz. Was Du meinst, ist
der Zugriff auf z.B. GUI-Elemente. Die sollten immer über
invokeAndWait() bzw. invokeLater() geschehen.
Post by Ernst Baumann
, außer man regelt das mit synchronized.
Ist das richtig?
Ich denke, synchronized nützt da nichts. Im Prinzip müssten die
Zugriffsmethoden von Swing synchronized sein. Sind sie jedoch nicht, da
Swing nicht multithread-fähig ist. Also helfen AFAIK nur o.g. Methoden.
Die Notwendigkeit solcher Klimmzüge besteht zum Glück nicht ganz so oft,
da ja häufig Methodenaufrufe der GUI direkt im EDT veranlasst sind, z.B.
beim Aufruf von event handlern. Diese laufen jedoch bereits im EDT,
können also prinzipiell* gefahrlos auf die gesamte GUI zugreifen.

[...]

* "prinzipiell" weil: rechenzeitintensive Berechnungen im EDT frieren
die GUI kurzfristig ein.


ciao

Marcus
Wolfgang Zitzelsberger
2009-02-27 08:08:45 UTC
Permalink
Post by Marcus Woletz
[...]
Post by Ernst Baumann
2)
a)
Von außerhalb des EDT soll nicht auf bereits realisierte
Swing/AWT-Objekte zugegriffen werden.
Korrekt.
Da das immer noch in den Köpfen rumschwirrt, möchte ich das zum Anlass
nehmen, um nochmal darauf hinzuweisen, dass dies seit einiger Zeit nicht
mehr richtig und auch für nicht realisierte Swing Komponenten gilt. Der
Begriff "realisiert" sollte in diesem Zusammenhang eventuell genauer
definiert werden.

Zitat:
"Initially there was a rule that it is safe to create and use Swing
components until they are realized but this rule is not valid any more,
and now it is recommended to interact with Swing from EDT only"

Quelle:
http://weblogs.java.net/blog/alexfromsun/archive/2006/02/debugging_swing.html

Wer Probleme mit dem EDT vermutet (und auch alle anderern ;-)), sollte
seine Anwendung mit dem CheckThreadViolationRepaintManager
(https://swinghelper.dev.java.net/) testen.


Grüße,
Wolfgang
--
www.javasoft.de
Ernst Baumann
2009-02-27 10:45:57 UTC
Permalink
Post by Marcus Woletz
Post by Ernst Baumann
Das wird doch aber bei meinem Programm im Main Thread gemacht, z.B.
diagramm
f
Wenn Du ein klein wenig Glück hast, funktionieren diese Zugriffe in
main(). Das ist jedoch ein wenig Glückssache, und deshalb solltest Du
diese Aufrufe in main() in ein Runnable packen. Steht auch so im Java
Tutorial.
Bedeutet das, dass mein kleines Demo-Programm also korrekt ist?
Post by Marcus Woletz
Post by Ernst Baumann
b)
Man darf eigentlich gar nicht von außerhalb, also von einem anderen
Thread auf den EDT zugreifen
Der EDT ist keine Datenstruktur, sondern ein Objekt, das ausgeführt
wird. Deshalb verstehe ich die Frage so nicht ganz. Was Du meinst, ist
der Zugriff auf z.B. GUI-Elemente. Die sollten immer über
invokeAndWait() bzw. invokeLater() geschehen.
Habe mich falsch ausgedrückt, deshalb folgende Korrektur:
Ich meine parallele Zugriffe vom EDT und Main-Thread auf gemeinsame
Variable (Speicherbereiche), wobei mindestens ein Zugriff ein
Schreibzugriff ist (d.h. Lese-Lesezugriff ist erlaubt,
Schreib-Lesezugriff nicht erlaubt, Schreib-Schreibzugriff nicht
erlaubt).
Post by Marcus Woletz
Post by Ernst Baumann
, außer man regelt das mit synchronized.
Ist das richtig?
Ich denke, synchronized nützt da nichts. Im Prinzip müssten die
Zugriffsmethoden von Swing synchronized sein. Sind sie jedoch nicht, da
Swing nicht multithread-fähig ist. Also helfen AFAIK nur o.g. Methoden.
Da die Zugriffsmethoden von Swing nicht synchronized sind (also ein
paralleler Zugriff von 2 verschiedenen Threads zu Problemen führt),
wäre es doch sinnvoll _diejenigen_ Teile in main, die Swing
_verwenden_, mit synchronized zu schützen (wie z.B. anlegen eines
Buttons, Farbe eines Buttons verändern, usw.).
Ist das richtig?

Also folgendes Beispiel:
main()
//schützen
Anweisung1, die Swing verwendet
//Ende schützen
i++
//schützen
Anweisung2, die Swing verwendet
//Ende schützen

mfg
Ernst
Loading...