![]() |
|
|
|||
|
|||||||
| Hilfe | Registrieren | Blogs | Mainboarddatenbank | Galerie | Extras | Suchen | Heutige Beiträge | Alle Foren als gelesen markieren |
![]() |
|
|
Themen-Optionen | Ansicht |
|
|
Posting #1 (im Thread / einzeln) |
|
bill
Commodore
Special ![]() Registriert seit: 18.02.2003
Beiträge: 400
|
Problem mit synchronized in JAVA
Hallo an alle!
Ich habe hier ein Problem mit JAVA und komme einfach nicht dahinter, warum es nicht funktioniert. Das Programm hat folgende Aufgabe : Es werden zwei Threads erstellt (Writer und Reader), die auf einen Stack (eigene Klasse) lesen und schreiben sollen. Die Stack-Operationen wurden dabei mit "synchronized" in einen Monitor gepackt, damit sie sich nicht in die Quere kommen. Leider funktioniert das nicht ganz! Und zwar gibt es in der Stack Klasse eine print-Funktion, welche den Inhalt des Stacks ausgibt. Diese Funktion ist ebenfalls "synchronized". Trotzdem funken sich die Threads da dazwischen! Sprich, die Reihenfolge der Ausgabe wird verändert! Das dürfte aber gerade nicht passieren! Der ganze Vorgang sollte folgendermaßen ablaufen: Wenn der Reader ein Element aus dem Stack liest, gibt er es aus. Wenn der Writer ein Element in den Stack reinschiebt, gibt er den gesamten Inhalt des Stacks aus. Beispiel : So sollte es sein : Code:
Inhalt Stack: [ 10 ] Stackelement geholt: 10 Inhalt Stack: [ 7 ] Stackelement geholt: 7 Inhalt Stack: [ 7 ] Inhalt Stack: [ 7 9 ] Stackelement geholt: 9 Stackelement geholt: 7 Reader:Stack ist leer Code:
Inhalt Stack: [ 10 ] Stackelement geholt: 10 Inhalt Stack: [ 7 ] Inhalt Stack: [ Stackelement geholt: 10 7 ] Inhalt Stack: [ 7 9 ] Stackelement geholt: 9 Stackelement geholt: 7 Reader:Stack ist leer Stack-Klasse (print Funktion rot markiert): Code:
public class Stack
{
private int[] refIntarr;
private int top = 0;
// Dem Konstruktor soll die Groesse des Stacks
// als Parameter uebergeben werden
public Stack(int groesse)
{
refIntarr = new int[groesse];
}
// Die Methode print() gibt den Inhalt des Stacks
// auf dem Bildschirm aus
public synchronized void print()
{
System.out.print("Inhalt Stack: [ ");
for (int i = 0; i < top; i++)
{
System.out.print(refIntarr[i] + " ");
}
System.out.println("]");
}
// Element mit push() auf Stack ablegen und
// OverflowException werfen, wenn Stack bereits voll
public synchronized void push(int zahl) throws OverflowException
{
if (top == refIntarr.length)
{
throw new OverflowException();
}
refIntarr[top] = zahl;
top++;
}
// Element mit pop() von Stack holen und
// UnderflowException werfen, wenn Stack bereits leer
public synchronized int pop() throws UnderflowException
{
if (top == 0)
{
throw new UnderflowException();
}
top--;
return refIntarr[top];
}
}
Code:
class Reader implements Runnable
{
Stack stackref = null;
// Konstruktor von Reader
public Reader(Stack stackref)
{
this.stackref = stackref;
}
public void run()
{
// In einer Endlosschleife Stackinhalte auf Bildschirm
// ausgeben, dann vom Stack ein Element holen und seinen Wert
// in einer neuen Zeile auf dem Bildschirm ausgeben
while (true)
{
// Im catch-Handler bei Stack-Unterlauf ausgeben:
// "Reader: Stack ist leer"
try
{
System.out.println("Stackelement geholt: " + stackref.pop());
//System.out.print("Reader: ");
//stackref.print();
}
catch (UnderflowException e)
{
System.out.println("Reader:" + e.getMessage());
}
// 3 ms schlafen legen. Vielleicht wird der Stack
// inzwischen gefuellt.
try
{
Thread.sleep(3);
}
catch (InterruptedException e)
{
System.out.println("Reader interrupted!");
return;
}
}
}
}
Code:
class Writer extends Thread
{
Stack stackref = null;
// Konstruktor von Writer
public Writer(Stack stackref)
{
this.stackref = stackref;
}
// Definition der run-Methode
public void run()
{
// In Schleife 10 Zufallszahlen auf den Stack schreiben
// Nach jedem Schreiben soll der Inhalt des Stacks auf
// dem Bildschirm ausgegeben werden.
for (int lv = 0; lv < 10; lv++)
{
int zahl = (int) (Math.random() * 10) + 1;
// Ist der Stack voll, so soll auf den Bildschirm
// ausgegeben werden: "Writer: Stack ist voll" und die
// Schleife abgebrochen werden.
try
{
stackref.push(zahl);
stackref.print();
}
catch (OverflowException e)
{
System.out.println("Writer:" + e.getMessage());
}
//Thread für 3ms schlafen legen
try
{
Thread.sleep(3);
}
catch (InterruptedException e)
{
System.out.println("Writer interrupted!");
return;
}
}
}
}
Code:
//File: OverflowException.java
//Klasse wird von Exception abgeleitet
class OverflowException extends Exception
{
//Nicht beachten
private static final long serialVersionUID = 1L;
//Parameterloser Konstruktor ruft Konstruktor der Klasse
//Exception mit Übergabeparameter einen String mit der Fehlermeldung
public OverflowException()
{
super("Stack ist voll");
}
}
Wäre euch echt dankbar, wenn mir jemand helfen könnte!! Schöne Grüße, bill |
|
|
Posting #2 (im Thread / einzeln) |
|
Georg
Admiral
Special ![]() Registriert seit: 11.11.2001
Ort: Leipzig
Beiträge: 1.495
|
Doch, sie können sich dazwischen funken, da das print nicht in der gleichen Sperre passiert.
das kannst du auf 2 Arten erreichen: 1. Du rufst das print() in der push und pop-Methode auf (also nicht extern). 2. Im writer schreibst du: Code:
synchronized(stackref) {
stackref.push(zahl);
stackref.print();
}
|
|
|
Posting #3 (im Thread / einzeln) | |
|
bill
Commodore
Special ![]() Registriert seit: 18.02.2003
Beiträge: 400
|
Zitat:
Vielen dank erstmal, dass du dir die Mühe gemacht hast, dass alles mal durchzuschauen! Irgendwie verstehe ich das aber nicht. Wieso ist denn die print Methode nicht in der gleichen Sperre (bzw. Monitor)?? Sie ist ja auch mit synchronized deklariert, was ja bedeutet, dass das Aufrufende Objekt (this) als Schlüssel genommen wird. Dasselbe habe ich ja auch bei den push und pop Methoden gemacht. Im Hauptprogramm wird nur ein Stackobjekt erstellt. Dessen Referenz wird jeweils dem Konstuktor des Readers bzw. Writers übergeben. Sie benutzen also dasselbe Objekt. Viele Grüße, bill |
|
|
|
Posting #4 (im Thread / einzeln) |
|
Georg
Admiral
Special ![]() Registriert seit: 11.11.2001
Ort: Leipzig
Beiträge: 1.495
|
Wenn du eine synchronized-Methode aufrufst, ist das Objekt gesperrt.
Während des push() ist das Objekt gesperrt. Nach dem push() wird der Lock wieder abgegeben . Nun wird das Objekt erneut gespert um das print() auszuführen. Dazwischen könnte aber ein pop() geschehen. Gleiches gilt für die pop()-Sache - das steht da zwar auf einer Zeile, trotzdem könnte zwischen dem pop() und dem System.out.println, das den Wert dann ausgibt, noch ein push() liegen. |
|
|
Posting #5 (im Thread / einzeln) | |
|
bill
Commodore
Special ![]() Registriert seit: 18.02.2003
Beiträge: 400
|
Zitat:
Die print() Methode gibt den Inhalt des Stacks in eckigen Klammern aus, also z.B. Code:
[ 0 1 2 ] Code:
[ 0 Reader: Stackelement geholt: 2 ] Gruß, bill |
|
|
|
Posting #6 (im Thread / einzeln) |
|
Georg
Admiral
Special ![]() Registriert seit: 11.11.2001
Ort: Leipzig
Beiträge: 1.495
|
Nein, das passiert bei folgendem Ablauf:
zuerst pop() Jetzt beginnt das print() - das gibt den Anfang aus Jetzt beginnt das println(... (von dem pop() ) und beendet sich jetzt wird das print() fortgesetzt. das Objekt ist immer korrekt gesperrt, trotzdem kommt die vermischte Ausgabe. das println( .... + pop() ) ist vom Ablauf ungefähr das gleiche wie a= pop(); println( .... + a) bau die Ausgaben mal direkt in die pop() und push() ein und du wirst keine Probleme mehr sehen also in die push() das print() und in die pop() das println(...) |
|
|
Posting #7 (im Thread / einzeln) | |
|
bill
Commodore
Special ![]() Registriert seit: 18.02.2003
Beiträge: 400
|
Sorry, bin grad irgendwie zu blöd für, kann dir nicht richtig folgen
Zitat:
bill |
|
|
|
Posting #8 (im Thread / einzeln) |
|
Georg
Admiral
Special ![]() Registriert seit: 11.11.2001
Ort: Leipzig
Beiträge: 1.495
|
Dein Fehler ist in Annahme 2.
Wenn das print() läuft ist das pop() schon fertig. Die Ausgabe, die dem pop() folgt ist aber nicht an den Monitor gebunden und kann sich damit mit den Ausgaben in print() überlagern. Zu deiner Frage 1: Du gibst die Artefakte des Stacks nach und nach aus - zwischendrin kann ein Thread-Wechsel stattfinden. |