Java Dining Philosophers, Semaphore - Probleme bei Wechsel der Deadlock-Strategie

The Terminator

Cadet 4th Year
Registriert
Dez. 2010
Beiträge
73
Hallo Leute!

Ich hoffe, ihr könnt mir hier weiterhelfen, ich sitze schon eine ganze Weile ohne weitergekommen zu sein.

Das Grundproblem: Wir sollen die Dining Philosophers in eine bereits vorhandene Swing-Applikation implementieren, wobei nur ein paar Codestellen/Methoden auszufüllen sind.
Das Ziel/Deadlock-Strategien ist, dass drei Teile erreicht werden (ich zitier aus der Angabe):

1. Nur Synchronisation, keine Deadlock Vermeidung
2. Deadlock-Vermeidung durch unterlassen von Hold-and-Wait
3. Deadlock-Beseitigung

Implementieren soll man dies mittels Semaphoren (oder auch Lock).

So weit so gut. Ich habe auch nun alles gemacht, hier der Code meiner Methoden:

Klasse Philosophers (Unterklasse von Thread, die einzelnen Schritte, die laut Angabe durchgeführt werden müssen, sind mit 1. bis 7. kommentiert):

Code:
public void run()
    {
    	while (true) { // just continue forever (= 7.)
    		try {
    			// 1. thinking
    			waiting = false;
    			delay();
    			
    			// 2. waiting for left chopstick
				addInfoText("waits for left chopstick to become available");
				waiting = true;
				// always (regardless of the currently active strategy) try to acquire the
				// left chopstick (wait if it is currently held by another philosopher)
				left.grab(this, false);
				addInfoText("grabbed left chopstick"); // includes tableChanged
				waiting = false;
				
				// 3. thinking
				delay();
				
				// 4. waiting for right chopstick
				addInfoText("waits for right chopstick to become available");
				waiting = true;
				// try to acquire the right chopstick; if the currently active strategy is
				// "prevention", only try to acquire it if it is available (not held by
				// another philosopher) - otherwise wait (which is enabled by "false")
				if (!right.grab(this, table.isStrategyPrevent())) {
					left.release(this); // release the currently hold left chopstick
					addInfoText("could not get right chopstick - put back left chopstick");
					continue; // start thinking again (includes delay)
				}
				addInfoText("grabbed right chopstick"); // includes tableChanged
				waiting = false;
				
				// 5. eating
				eating = true;
				delay();
				eating = false;
				
				// 6. putting back left and right chopstick
				right.release(this);
				addInfoText("released right chopstick"); // includes tableChanged
				left.release(this);
				addInfoText("released left chopstick"); // includes tableChanged
				
			} catch (InterruptedException e) { // Zen-Master was active (strategy "detect/recover")
				left.release(this); // forcefully release the currently hold left chopstick
				addInfoText("was forced to put back left chopstick"); // includes tableChanged
				waiting = false;
				continue; // start thinking again (includes delay)
			}
    	}
    }

Klasse Chopstick:
Code:
private final Semaphore s = new Semaphore(1);

public boolean grab(Philosopher philo, boolean preventDeadlock) throws InterruptedException {
    	if (philo == null) {
    		throw new IllegalArgumentException("Philosopher must not be null.");
    	}
    	
		// only check the current owner of the chopstick if the currently active strategy
		// is "prevention"; otherwise skip this part and continue at the standard procedure
		if (preventDeadlock) {
    		if (owner != null) {
    			return false; // would have to wait for the chopstick
    		} // else continue below (standard procedure)
    	}
    	
		// standard procedure (regardless of currently active strategy)
		s.acquire();
		owner = philo;
		return true;
    }

public void release(Philosopher philo) {
    	if (philo == null || owner != philo) {
    		throw new IllegalArgumentException("Philosopher must not be null and must hold the chopstick.");
    	}
    	
    	// make the chopstick available/"grab-able" again
    	owner = null;
    	s.release();
    }

Das vorgegebene Hauptprogramm macht im Wesentlichen die grafische Darstellung. Als Initialisierung werden stets fünf Chopsticks und fünf neue Philosophers erzeugt und gestartet. Folgende Operationen sind in der GUI verfügbar:

> Swing-Radio-Box: Umschalten der drei Strategien mit anschließender Betätigung des Buttons (dabei wird eine komplette Neuinitialisierung der Chopsticks und Philosophers veranlasst)
> Anzeige des Tisches (mit Chopsticks und Philosophers)
> Anzeige einer Text-Infobox (da kommen die Nachrichten der Philosophers-Klasse mittels addInfoText(String) hinein)


Und nun mein Problem: Es funktioniert eigentlich alles wunderbar, außer wenn ich im laufenden Programm zwischen den Strategien wechsle. Dann kann (muss aber auch nicht!!!) z.B. folgendes passieren:

> Starte Programm mit Strategie 1 (nichts tun, muss Deadlock produzieren)
> Wechsle mitten drin auf Strategie 2 (Prevention)
> Nach einer kurzen Zeit wechsle wieder auf Strategie 1
> Jetzt kommt's: Es wird ein Deadlock produziert (Grafik --> alle Philosophers warten mit je einem Chopstick) ABER DENNOCH erscheinen in der Text-Infobox eine gewisse Zeit lang Nachrichten, dass Chopsticks genommen und wieder frei gegeben werden OBWOHL eigentlich eine Deadlock vorherrscht. Wie gerade gesagt, nach einer gewissen Zeit passt dann alles wieder.

Übrigens passiert das auch, wenn ich Strategie 1 laufen lasse und auf Re-Start (damit werden eben die Strategien gewechselt, außer man hat dieselbe eingestellt, dann wird die natürlich nur neu gestartet) drücke, BEVOR ein Deadlock eingetreten ist.

Ich hoffe, dass mein Problem so halbwegs verständlich ist.
Ich weiß echt nicht, woran das liegen könnte. Entweder bin ich einfach zu dumm oder übersehe grad irgendetwas.

Im Anhang lade ich die App (als gezippte .jar, da CB ohne zip das leider verhindert) hoch, den gesamten Code will ich eher nicht uploaden, da er ja größtenteils nicht von mir stammt. Wenn noch irgendetwas unklar ist oder jemand mehr Infos benötigt, dann werde ich mich natürlich bemühen, alles zu beantworten/ergänzen.


Vielen Dank schon im Voraus

LG


P.S.: weiß leider nicht, wie man den Code einklappbar macht (damit er nicht soviel Platz verbraucht)
 
Zuletzt bearbeitet:
The Terminator schrieb:
> Swing-Radio-Box: Umschalten der drei Strategien (dabei wird eine komplette Neuinitialisierung der Chopsticks und Philosophers veranlasst)
Bei mir wird beim Wechsel nichts neu initialisiert. Wenn nach Strategie 1 ein Deadlock produziert wird, ändert ein Wechsel auf eine andere Strategie nichts daran.
 
Ah, tschuldigung, hab ich vergessen zu erwähnen: man muss die Auswahl dann mit dem Button bestätigen, erst danach wird die andere Strategie aktiv.
 
OK, jetzt kann ich es reproduzieren, es "funktioniert" aber nicht immer. Interessantes Log, wenn ich auf Strategie 1 zurückschalte:
Code:
Starting over ...
[Philosopher: 2]: waits for left chopstick to become available
[Philosopher: 2]: grabbed left chopstick
[Philosopher: 4]: waits for left chopstick to become available
[Philosopher: 4]: grabbed left chopstick
[Philosopher: 5]: released right chopstick <--- woher hat der die?
Für mich sieht das so aus, als ob der Neustart nicht korrekt implementiert ist. Bei deinem Code fehlt z.B. die Abbruchbedingung, was den Thread ja theoretisch ewig weiterlaufen lässt.
 
Ah ok! Danke schon einmal!

Blöd ist halt nur, dass ich die Neuinitialisierung nicht beeinflussen sollte. Und wie sollte ich meine Abbruchbedingung einbauen? Es kommt da ja irgendwie keine Nachricht. Sowas Blödes.
Der vorgegebene Code sieht übrigens so aus:

Code:
/**
     * User pressed the restart button.
     * Restarts the animation but creating all objects afresh.
     */
    private void restart() {
    	clearInfoText();
    	doStartAnimation();
    	table.repaint();
    }

/**
     * (Re-) start the animation
     */
    private void doStartAnimation() {
        sticks = new Chopstick[count];
        
        for(int i=0; i< count; i++)
            sticks[i]=new Chopstick();
        
        philo = new Philosopher[count];
        for(int i=0; i < count; i++)
            philo[i] = new Philosopher(i+1, this,sticks[i], sticks[(i+1)%count]);
        
        for(int i=0; i < count; i++)
            philo[i].start();    	
    }

Gibts da irgendeine Möglichkeit, wie ich meine Threads beende ohne, dass von außen (also durch den Button) ein Signal kommt (was es in dieser Form sowieso eigentlich gar nicht tut)?
 
Der restart Code hält die alten Threads nicht an. Daher laufen diese im Hintergrund weiter, bis sich dort auch ein Deadlock bildet.

Die einzige Möglichkeit die hier sehe, ohne den restart Code zu ändern, wäre dass sich jeder Thread den Wert von table.isStrategyPrevent() beim Start merkt und sich bei einer Änderung einfach beendet.

Idealerweise sollte dies mit einer Variable boolean doStop o.Ä. gelöst werden, die vom Hauptthread gesetzt und von den Endlosthreads regelmäßig angefragt wird. Komisch, dass da keiner dran gedacht hat. Wer hat den Code denn geschrieben? Hoffentlich nicht jemand, der sich mit Threads auskennt :D
 
Danke! Werde ich dann am Abend testen.

Bezüglich Autor: ist ein Prof an der Uni.......... (Programm ist aber aus 2003, allerdings glaub ich, dass er da auch schon zumindest mit dem Studium fertig war)


EDIT: Vielen Dank noch einmal für deine Hilfe! Es lag genau daran! (Danke auch an Green Mamba an dieser Stelle)
 
Zuletzt bearbeitet:

Ähnliche Themen

Zurück
Oben