Hallo,
wir hatte hier die Tage mal ein Thema wo es um synchronized-Blöcke ging.
Hatte mich im nachher noch etwas damit beschäftigt und mir ist eine Frage dazu gekommen.
Hab das Beispiel nochmal sinngemäß nachgebaut:
Nun soll das Beispiel also einen typischen Büro-Alltag simulieren. Es werden immer ein paar Zettel (10) von einer beliebigen Ablage genommen und dann auf irgendeine andere Ablage verschoben.
Es sollten natürlich keine Zettel abhanden kommen (vieleicht doch nicht so realistisch^^).
Möglichkeit1:
Die Betriebsmethoden werden komplett synchronisiert.
Funktioniert, ist aber nicht gut, weil wenn ein Arbeiter eine Aktion durchführt ist immer der ganze Betrieb lahmgelegt.
Möglichkeit 2:
Sollte besser sein und funktioniert auch soweit.
Allerdings hatte ich zuerst die getter/setter Methoden von Ablage synchronisiert.
Das hat dann allerdings nicht funktioniert.
Woran liegt das?
Meine Vermutung:
Würde man den Aufruf der getter/setter so schreiben ist das Problem offensichtlich.
Ist also dieser Aufruf das gleiche wie oben?
Was macht der Compiler daraus, bzw wie wird das zur Laufzeit ausgeführt?
Wann hat der aufrufende Thread den Monitor auf die Ablage und wann wird er freigegeben?
Ist es der Grund für das Problem,dass der Monitor trotzdem zwischen get und set nochmal freigegeben werden kann, oder übersehe ich noch etwas anderes?
Ich hoffe es ist einigermaßen klar geworden was ich wissen möchte. Ist etwas lang geworden.
Vielen Dank im Vorraus
wir hatte hier die Tage mal ein Thema wo es um synchronized-Blöcke ging.
Hatte mich im nachher noch etwas damit beschäftigt und mir ist eine Frage dazu gekommen.
Hab das Beispiel nochmal sinngemäß nachgebaut:
Java:
public class Ablage {
int zettelAnzahl;
public Ablage(int zettelAnzahl) {
super();
this.zettelAnzahl = zettelAnzahl;
}
public int getZettelAnzahl() {
return zettelAnzahl;
}
public void setZettelAnzahl(int zettelAnzahl) {
this.zettelAnzahl = zettelAnzahl;
}
}
Java:
public class Arbeiter extends Thread {
Betrieb meinBetrieb;
public Arbeiter(Betrieb meinBetrieb) {
super();
this.meinBetrieb = meinBetrieb;
}
public Betrieb getMeinBetrieb() {
return meinBetrieb;
}
public void setMeinBetrieb(Betrieb meinBetrieb) {
this.meinBetrieb = meinBetrieb;
}
@Override
public void run() {
while( !isInterrupted() )
{
meinBetrieb.nehmeZettel(10);
meinBetrieb.legeZettelab(10);
try
{
Thread.sleep(100);
}
catch(InterruptedException e)
{
interrupt();
}
}
}
}
Java:
package syncBeispiel;
public class Betrieb {
Ablage[] ablagen;
int anzahlAblagen;
public Betrieb(int anzahlAblagen, int zettelpAblage) {
super();
this.anzahlAblagen=anzahlAblagen;
this.ablagen = new Ablage[anzahlAblagen];
for(int i=0; i<ablagen.length;i++){
ablagen[i]=new Ablage(zettelpAblage);
}
}
public void nehmeZettel(int anzahl){
Ablage p=ablagen[(int) (Math.random()*this.anzahlAblagen)];
p.setZettelAnzahl(p.getZettelAnzahl()-anzahl);
}
public void legeZettelab(int anzahl){
Ablage p=ablagen[(int) (Math.random()*this.anzahlAblagen)];
p.setZettelAnzahl(p.getZettelAnzahl()+anzahl);
}
public void pruefen(){
int sum=0;
for(Ablage p:ablagen){
sum+=p.getZettelAnzahl();
}
System.out.println(sum);
}
}
Java:
public class SyncTest {
/**
* @param args
*/
public static void main(String[] args) {
Betrieb betrieb = new Betrieb(5, 45);
Arbeiter[] arbeiter = new Arbeiter[50];
for (int i = 0; i < arbeiter.length; i++) {
arbeiter[i] = new Arbeiter(betrieb);
arbeiter[i].start();
}
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
}
for(int i = 0; i < 50; i++)
{
arbeiter[i].interrupt();
try {
arbeiter[i].join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
betrieb.pruefen();
}
}
Nun soll das Beispiel also einen typischen Büro-Alltag simulieren. Es werden immer ein paar Zettel (10) von einer beliebigen Ablage genommen und dann auf irgendeine andere Ablage verschoben.
Es sollten natürlich keine Zettel abhanden kommen (vieleicht doch nicht so realistisch^^).
Möglichkeit1:
Die Betriebsmethoden werden komplett synchronisiert.
Java:
public synchronized void nehmeZettel(int anzahl){
Ablage p=ablagen[(int) (Math.random()*this.anzahlAblagen)];
p.setZettelAnzahl(p.getZettelAnzahl()-anzahl);
}
public synchronized void legeZettelab(int anzahl){
Ablage p=ablagen[(int) (Math.random()*this.anzahlAblagen)];
p.setZettelAnzahl(p.getZettelAnzahl()+anzahl);
}
Möglichkeit 2:
Java:
public void nehmeZettel(int anzahl){
Ablage p=ablagen[(int) (Math.random()*this.anzahlAblagen)];
synchronized(p){
p.setZettelAnzahl(p.getZettelAnzahl()-anzahl);
}
}
public void legeZettelab(int anzahl){
Ablage p=ablagen[(int) (Math.random()*this.anzahlAblagen)];
synchronized(p){
p.setZettelAnzahl(p.getZettelAnzahl()+anzahl);
}
}
Allerdings hatte ich zuerst die getter/setter Methoden von Ablage synchronisiert.
Java:
public synchonized int getZettelAnzahl() {
return zettelAnzahl;
}
public synchronized void setZettelAnzahl(int zettelAnzahl) {
this.zettelAnzahl = zettelAnzahl;
}
Woran liegt das?
Meine Vermutung:
Würde man den Aufruf der getter/setter so schreiben ist das Problem offensichtlich.
Java:
public void nehmeZettel(int anzahl) {
Ablage p = ablagen[(int) (Math.random() * this.anzahlAblagen)];
int number = p.getZettelAnzahl();
p.setZettelAnzahl(number - anzahl);
}
public void legeZettelab(int anzahl) {
Ablage p = ablagen[(int) (Math.random() * this.anzahlAblagen)];
int number=p.getZettelAnzahl();
p.setZettelAnzahl(number + anzahl);
}
Ist also dieser Aufruf das gleiche wie oben?
Java:
p.setZettelAnzahl(p.getZettelAnzahl() + anzahl);
Wann hat der aufrufende Thread den Monitor auf die Ablage und wann wird er freigegeben?
Ist es der Grund für das Problem,dass der Monitor trotzdem zwischen get und set nochmal freigegeben werden kann, oder übersehe ich noch etwas anderes?
Ich hoffe es ist einigermaßen klar geworden was ich wissen möchte. Ist etwas lang geworden.
Vielen Dank im Vorraus