Planet 3DNow! Forum    
  Fantastic Zero Logo


Zurück   Planet 3DNow! Forum > Software und Treiber > Programmierung
Hilfe Registrieren Blogs Mainboarddatenbank Galerie Extras Suchen Heutige Beiträge Alle Foren als gelesen markieren

Gehe zu
Antwort
 
Themen-Optionen Ansicht
Alt 17.01.2007, 17:26   Posting #1 (im Thread / einzeln)
bill
Commodore
Special
Commodore
 
Benutzerbild von bill
 
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
Und so sieht es (manchmal) aus:
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
Hier mal der Source Code:

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];

	}
}
Dann die Reader-Klasse (bzw Thread):
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;
			}

		}
	}
}
Die Writer-Klasse (bzw. Thread):
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;
			}

		}
	}
}
Die Overflow-Exception Klasse (Underflow sieht entsprechend aus):
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");
    }
}
Falls ihr noch mehr Infos braucht, einfach fragen!

Wäre euch echt dankbar, wenn mir jemand helfen könnte!!

Schöne Grüße,
bill
 
Alt 18.01.2007, 12:35   Posting #2 (im Thread / einzeln)
Georg
Admiral
Special
Admiral
 
Benutzerbild von Georg
 
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();
	}
 
Alt 18.01.2007, 18:04   Posting #3 (im Thread / einzeln)
bill
Commodore
Special
Commodore
 
Benutzerbild von bill
 
Registriert seit: 18.02.2003
Beiträge: 400
Zitat:
Zitat von Georg Beitrag anzeigen
Doch, sie können sich dazwischen funken, da das print nicht in der gleichen Sperre passiert.
Hallo Georg!

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
 
Alt 19.01.2007, 14:48   Posting #4 (im Thread / einzeln)
Georg
Admiral
Special
Admiral
 
Benutzerbild von Georg
 
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.
 
Alt 19.01.2007, 16:21   Posting #5 (im Thread / einzeln)
bill
Commodore
Special
Commodore
 
Benutzerbild von bill
 
Registriert seit: 18.02.2003
Beiträge: 400
Zitat:
Zitat von Georg Beitrag anzeigen
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.
Das ist ja auch in Ordnung, das stört mich nicht. Aber du sagst ja selber, die print() Methode sperrt das stack-Objekt um ausgeführt zu werden. Und genau da drin passiert es ja, dass jemand dazwischenfunkt!

Die print() Methode gibt den Inhalt des Stacks in eckigen Klammern aus, also z.B.
Code:
[ 0 1 2 ]
Dadurch das jemand dazwischenfunkt (hier der Reader), sieht dann die Ausgabe z.B. so aus
Code:
[ 0 Reader: Stackelement geholt: 2
]
Es wird also die eigentlich gesperrte print()-Methode unterbrochen!

Gruß,
bill
 
Alt 19.01.2007, 17:04   Posting #6 (im Thread / einzeln)
Georg
Admiral
Special
Admiral
 
Benutzerbild von Georg
 
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(...)
 
Alt 19.01.2007, 18:22   Posting #7 (im Thread / einzeln)
bill
Commodore
Special
Commodore
 
Benutzerbild von bill
 
Registriert seit: 18.02.2003
Beiträge: 400
Sorry, bin grad irgendwie zu blöd für, kann dir nicht richtig folgen

Zitat:
Zitat von Georg Beitrag anzeigen
Nein, das passiert bei folgendem Ablauf:

zuerst pop()

Jetzt beginnt das print() - das gibt den Anfang aus
(1) Meinst du das Stack.Print() vom Writer? Wieso gibt es nur den Anfang aus und hört dann auf??
Jetzt beginnt das println(... (von dem pop() ) und beendet sich
(2) Eben, und hier ist ja noch das Stack.Print() in Aktion, wieso darf pop() das Objekt nehmen, es ist doch gesperrt??
jetzt wird das print() fortgesetzt.
(3) siehe (1)
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)
Klar, die Ausgabe kommt erst nach beendeten pop()

bau die Ausgaben mal direkt in die pop() und push() ein und du wirst keine Probleme mehr sehen
Ich glaub dir schon, dass es so funktionieren würde, ich will halt nur verstehen, wo ich grad 'nen Logikfehler habe
also in die push() das print() und in die pop() das println(...)
Grüße,
bill
 
Alt 19.01.2007, 22:00   Posting #8 (im Thread / einzeln)
Georg
Admiral
Special
Admiral
 
Benutzerbild von Georg
 
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.
 
  Antwort
 

Themen-Optionen
Ansicht

Gehe zu


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:20 Uhr.



Powered by vBulletin® Version 3.8.7 (Deutsch)
Copyright ©2000 - 2013, vBulletin Solutions, Inc.
Inhalte und Bilder - Copyright ©1999 - 2013 - Planet 3DNow!