Übung 1.4: Thread-Engpass auflösen
Bevor Sie mit dieser Übung beginnen, sollten Sie Übung 1.3: Thread-Engpass feststellen ausgeführt haben.
Für die Suche nach der gegenseitigen Sperre in Ihrem Code können Sie neben der Thread-Ansicht das UML2-Folgediagramm
(Ansichten "Objektinteraktionen" und "Thread-Interaktionen") sowie die
Profilerstellungsressource "Aufruf-Stack" im Monitor für Profilerstellung verwenden.
Um diese gegenseitige Sperre aufzulösen, wollen wir zunächst herausfinden, welche Methodenaufrufe und Objekte zu diesem Problem beitragen:
- Suchen Sie in der Thread-Ansicht den ersten Thread philo*, der in den Status "Wartet auf Sperre" eintritt. Bewegen Sie den Cursor auf das Segment "Wartet auf Sperre". Die Kurzinfo gibt die Sperre an, auf die der Thread wartet: Fork.<ID-Nummer>.
- Klicken Sie mit der rechten Maustaste auf die Profilerstellungsressource für den Durchgang und wählen
Sie Öffnen mit > UML2-Objektinteraktionen aus. Daraufhin wird die Ansicht "UML2-Folgediagramm" mit den Objektinteraktionen
geöffnet.
- Blättern Sie im Folgediagramm zur Sperre Fork.<ID-Nummer> vor und klicken Sie doppelt auf die Sperre, um sie auszuwählen.
- Blättern Sie die Anzeige vor und suchen Sie nach einem waagerechten
Pfeil von einem der philo*-Threads zu Fork.<ID-Nummer>. Diese Pfeile zeigen die Interaktionen zwischen Objekten an.
Die erste Interaktion zwischen diesen beiden Objekten gibt an, dass der philo*-Thread Fork.<ID-Nummer> angefordert hat. Der zugehörige Pfeil hat die Beschriftung getName.
- Klicken Sie doppelt auf getName. In der Thread-Ansicht verschiebt sich daraufhin die vertikale Markierung für die aktuelle Zeit, um
die Vorgänge im gesamten Programm beim Aufruf von getName anzuzeigen. Sie können sehen, dass die Anforderung erfolgreich war, denn der
philo*-Thread, der die Anforderung gesendet hat, tritt nicht in den Status "Wartet auf Sperre" ein.
- Klicken Sie im Folgediagramm erneut auf Fork.<ID-Nummer> und blättern Sie
in der Anzeige bis zu einer Anforderung getName von einem anderen philo*-Thread vor.
- Klicken Sie doppelt auf diese Instanz von getName. Die Markierung für die aktuelle Zeit zeigt an, dass
der philo*-Thread, der die Anforderung sendet, Fork.<ID-Nummer> nicht abrufen kann und in den
Status "Wartet auf Sperre" eintritt.
Bei dieser Instanz besteht das Problem in der Anforderung nach einer Sperre "Fork", die von einem anderen Thread gehalten wird. Überprüfen Sie die übrigen Threads, die am Ende in den Status "Wartet auf Sperre" wechseln, um
festzustellen, ob dies auch für andere Instanzen gilt.
Jetzt wollen wir die Methode finden, die das Problem verursacht:
- Klicken Sie mit der rechten Maustaste auf die Profilerstellungsressource für den Durchgang und wählen
Sie Öffnen mit > UML2-Thread-Interaktionen aus. Daraufhin wird die Ansicht "UML2-Folgediagramm" mit den Thread-Interaktionen
geöffnet.
- Erweitern Sie im Monitor für Profilerstellung die Anzeige für Ihre Profilerstellungsressource. Erweitern Sie dann die Anzeige
für die Einträge "Thread-Analyse" und "Aufruf-Stack".
- Klicken Sie in der Thread-Ansicht mit den Namen der Threads doppelt auf den
ersten philo*-Thread, der in den Status "Wartet auf Sperre" eintritt. In der Ansicht "Thread-Interaktionen" werden jetzt nur Informationen zu diesem Thread angezeigt.
- Blättern Sie zum Ende der Informationen für den Thread vor und klicken Sie doppelt auf die letzte vom Thread ausgeführte Methode:
die Methode run. Der Aufruf-Stack im Monitor für Profilerstellung zeigt alle zu diesem Zeitpunkt ausgeführten Aufrufe im Stack an.
- Im Aufruf-Stack sehen Sie, dass der Thread, der die Sperre hält, die Methode
Sleep in Philosopher.java aufgerufen hat oder ebenfalls auf eine Sperre wartet und somit keine weiteren Aktionen ausführt.
- Überprüfen Sie die übrigen Threads, die am Ende des Durchgangs auf eine Sperre warten. Die Methode Sleep in Philosopher.java ist oft im Aufruf-Stack enthalten und könnte das Problem sein.
Da die Methode Sleep als Fehlerursache vermutet wird, wollen wir uns den Code anschauen:
- Klicken Sie im Aufruf-Stack mit der rechten Maustaste auf eine Instanz von Sleep(int) void [Philosopher.java] und wählen
Sie Quelle öffnen aus. Die Quelle wird im Editor an der Position der Klasse Sleep geöffnet.
- Untersuchen Sie den Code. Beachten Sie, dass die Methode Sleep aus der Methode run heraus aufgerufen wird. Zunächst wird trace aufgerufen, um die Nachricht "got left..." auszugeben. Anschließend
wird Sleep aufgerufen. Wenn wir den Aufruf von Sleep auf Kommentar setzen, lässt sich die gegenseitige Sperre möglicherweise vermeiden.
- Setzen Sie Sleep auf Kommentar.
- Wählen Sie Datei > Speichern aus.
Erstellen Sie jetzt erneut das Profil für Ihr Programm.
Diesmal wird das Programm ohne eine gegenseitige Sperre ausgeführt und gibt folgendes auf der Konsole aus:
HeadWaiter reports all philosophers have finished dining normally
Wie Sie sehen, zeigen die Thread-Ansicht und andere Ansichten an, was während der Programmausführung mit den
Threads geschieht. Ihre Aufgabe ist es, ausgehend von Ihrer Kenntnis des Programms die Analyse durchzuführen und die gegenseitige
Sperre aufzulösen.
Lesen Sie zum Abschluss des Lernprogramms die Informationen in der Zusammenfassung.