Zum Inhalt springen

  • Beiträge
    72
  • Kommentare
    239
  • Aufrufe
    12.710

Modul: Nichtsequentielle Programmierung


kurtchen

928 Aufrufe

Einordnung und Voraussetzungen

 

Das Modul "nichtsequentielle Programmierung" (NSP) ist für Studierende im Studiengang "Web- und Medieninformatik" das letzte im Fachgebiet "Grundlagen der Informatik und Programmierung". Springer Campus empfiehlt, es im 5. Semester zu belegen. Die Module "Grundlagen der Informatik 1 & 2" werden als inhaltliche Voraussetzung genannt. Konkret heißt das, dass man Grundkenntnisse in strukturierter, prozeduraler und objektorientierter Programmierung in Java braucht. Auch Kenntnisse der generischen Programmierung, wie sie GdI2 vermittelt, sind unbedingt nötig.

 

Meiner Meinung nach reicht das aber nicht. Ich möchte ergänzen, dass das Modul "Rechnerstrukturen und Betriebssysteme" eine nötige Vorbereitung ist, weil man hier einiges an Hintergrundwissen über Speicher- und Prozessverwaltung lernt. Auch GdI3 sollte man meiner Meinung nach belegt haben, weil die Kursteile zu Nebenläufigkeit und GUIs eigentlich voraussetzen, dass man sich ein bisschen mit Swing und dem AWT auskennt. Schließlich halte ich auch GdI4 "Algorithmen und Datenstrukturen" für zumindest sinnvoll, weil es bei der nichtsequentiellen Programmierung auch um den nebenläufigen Zugriff auf verschiedene Datenstrukturen geht. Es ist sinnvoll, wenn man diese kennt und versteht.

 

Studierende im Studiengang Wirtschaftsinformatik müssen NSP nicht absolvieren, können es aber als Vertiefung belegen. Außerdem ist NSP ein Pflichtmodul der wissenschaftlichen Weiterbildung "Anwendungsprogrammierer". Das könnte für manche Studierende interessant sein, die sich unsicher sind, ob ihre Ausdauer für ein komplettes Bachelorstudium reicht.

 

Das Lehrbuch

 

Grundlage für den Kurs ist das Lehrbuch "Java: Nebenläufige und verteilte Programmierung" von Peter Ziesche und Doga Arinir. Es hat an die 360 Seiten, aber wegen recht umfangreicher Anhänge kommt der reine Kurstext auf nur ca. 330 Seiten, was für einen Springer Campus Kurs eher im unteren Mittelfeld liegt. Diese 330 Seiten haben es allerdings in sich. Ich empfinde NSP als das herausfordernste der Java-Module.

 

Der Titel ist Programm. Nebenläufige Programmierung heißt, dass es mehr als einen Kontrollfluss gibt. Verteilte Programmierung heißt, dass Teile einer Anwendung auf verschiedenen Rechnern ausgeführt werden, sozusagen Nebenläufigkeit plus Entfernung. Wichtig ist zunächst die grundlegende Unterscheidung zwischen Prozessen und Threads. Prozesses laufen in je eigenen Speicherbereichen, während sich Threads als leichtgewichtige Prozesse einen Speicherbereich teilen. Die Möglichkeit, so auf gemeinsamen Daten zu arbeiten, erleichtert einerseits die Kommunikation und Kooperation zwischen den Threads. Andererseits schafft das viele Probleme, die erst zur Laufzeit auftreten und schwer reproduzierbar sind.

 

Warum ist nebenläufige Programmierung schwierig?

 

Als Programmierer kann man sich vorstellen, dass nebenläufige Programmteile auf verschiedenen CPUs zur Ausführung kommen. In modernen Multicore-Systemen kann und wird das auch tatsächlich so sein. Da in modernen Betriebssystemen stets Dutzende von Prozessen laufen, werden nebenläufige Programmteile de facto häufig auf einer CPU oder einem Kern ausgeführt werden, der zwischen verschiedenen Threads umschaltet. Es ist nicht vorhersehbar, in welcher Reihenfolge die Threads zur Ausführung kommen werden. Wenn Threads eine gemeinsame Datenstruktur manipulieren, kann man also nicht vorhersagen, was in welcher Reihenfolge passieren wird. Genau hier liegt eine der großen Herausforderungen. Als Programmierer gewöhnt man sich nämlich zunächst an das schön planbare Nacheinander eines Programmablaufs. Man trainiert sich an, in Sequenzen zu denken, Abläufe in Arbeitsschritte und Zustandsabfolgen zu zerlegen. Der Programmzustand ist so zu jedem Zeitpunkt das Ergebnis dessen, was zuvor passiert ist. Bei nebenläufigen Programmen ist das im Prinzip auch so, aber es gibt Ungewissheit, was in welcher Reihenfolge passieren wird. Man ist gezwungen, viel darüber nachzudenken, welche Ausführungsreihenfolgen möglich sind und welche Folgen das haben könnte. Auch wird das Testen eines Programmes schwieriger. Ein Programm kann bei Tests hervorragend laufen und im Dauerbetrieb kommt es zu bizarren Bugs, mit denen man nie gerechnet hätte. Nebenläufige Programmierung ist leider komplex.

 

Theoretische Konzepte

 

Kommen wir zum Aufbau des Kurses. Die ersten 60 Seiten sind eine Einführung in die nebenläufige Programmierung. Es beginnt mit grundlegenden Konzepten:
- Anwendungen und Prozesse
- Threads und Scheduling
- Speicherverwaltung mit Stapel und Halde, zunächst sequentiell
- und Speicherverwaltung bei mehreren Threads
- Vorteile von Nebenläufigkeit: Bessere Performance und bessere Reaktionszeiten, vor allen in Multicore-Umgebungen
- Probleme nebenläufiger Programme
- Synchronisation und kritische Abschnitte
- Monitore
- Lebendigkeit
- Verklemmungen

 

Insbesondere bei den Ausführungen zur Speicherverwaltung musste ich schon sehr konzentriert lesen, um nachvollziehen zu können, was genau auf Stapel und Halde z.B. bei der Initialisierung eines GUIs passiert. Auch wenn hier Codebeispiele in Java gegeben werden, geht es in diesem Kursteil nicht um die Programmiersprache Java sondern um Konzepte.

 

Die Umsetzung in Java

 

Diese Konzepte werden in den folgenden 60 Seiten mit Leben erfüllt, denn nun geht es um die Realisierung in Java. Man lernt:
- Threads zu erzeugen, indem man von Thread abgeleitete Klassen programmiert.
- Die Schnittstelle Runnable zu nutzen, um Aufträge einem Thread zur Ausführung zu übergeben.
- Gegenseitigen Ausschluss zu realisieren, indem man Methoden oder Codeabschnitte als synchronized kennzeichnet.
- Monitore in Java zu implementieren, in denen Threads schlafen gelegt werden, die auf Ressourcen warten müssen.
- Erste Strategien, um Verklemmungen zu vermeiden.

 

Das Kapitel endet mit einem Fallbeispiel "Generisches Suchsystem", an dem die neuen Konzepte in einer Anwendungssituation gezeigt werden sollen. Dieses Fallbeispiel, dass sich durch den Kurs zieht, ist einer meiner wenigen Kritikpunkte. Während das Fallbeispiel Auftragsverwaltung aus GdI3 komplett und ausführbar vorlag und ausprobiert werden konnte, ist das generische Suchsystem nicht lauffähig, weil nicht komplett ausprogrammiert. Man kann die Klassen compilieren, aber man kann es nicht in Aktion erleben. So hat man nur eine statische Sicht auf den Code: Das hat didaktisch zwar auch einen hohen Wert, aber mir hätte es geholfen, mit dem Fallbeispiel herumspielen zu können, den Code in der Ausführung zu erleben. Dieser Aspekt hat mir bei diesem Kurs gefehlt. Zum Glück gibt es viele andere Codebeispiele im Kurs, die ausführbar sind und zum Experimentieren einladen.

 

Modellierung von Nebenläufigkeit in UML

 

Es folgt ein kurzes Kapitel über die Modellierung von Nebenläufigkeit in der UML. In den sonst so wichtigen Klassendiagrammen ist sie nicht so gut darstellbar. Nebenläufigkeit ist etwas, dass sich zur Laufzeit entfaltet. Sie kann daher besser mit Aktivitätsdiagrammen, Sequenzdiagrammen und Zustandsautomaten dargestellt werden. Hier wird auch auf den Anhang verwiesen, bei dem noch einmal die Petri-Netze knapp wiederholt werden, die ich ja schon aus Softwaretechnik 1 kannte. Die Markenwanderung im Petri-Netz hilft nämlich, Aktivitätsdiagramme besser zu verstehen. Hier sieht man, dass die Module oft schön untereinander vernetzt sind. Dieses Kapitel ist kurz, aber für die Präsenzklausur sollte man sich das gut anschauen.

 

Vertiefende Kapitel zur Nebenläufigkeit in Java

 

Nun kommt ein Kapitel zum Entwurf nebenläufiger Anwendungen in Java. Dieses Kapitel ist eine Sammlung verschiedener Klassen, die beim Entwurf nebenläufiger Anwendungen hilfreich sein können. Sie stammen meist aus java.util.concurrent.


Man lernt hier vieles über:
- den Unterschied zwischen threadsicheren und nebenläufigen Klassen
- verschiedene Arten von Warteschlangen und Puffern
- Container mit copy-on-write-Semantik, deren Iteratoren bei nebenläufigen Schreibzugriffen weiter laufen können.
- verschiedene nebenläufige Container
- Auftragsdienste und Threadpools, die eine Anzahl von Threads zum Abarbeiten von Auftragen bereithalten.
- Aufträge mit Ergebnis, die etwas komplizierter zu handhaben sind.
- Flexible selbst implementierte Monitore mit eigenen Lock-Objekten und Conditions
- Leser-Schreiber-Synchronisation
- Atomare Operationen

 

Als Anwendungsbeispiele dienen hier nebenläufige Matrizenmultiplikation und ein nebenläufiger Quicksort. Diese Beispiele eigenen sich gut für eigene Experimente.

 

Ein Problem mit diesem Kursteil ist: Die einzelnen Klassen werden recht knapp vorgestellt. Die Codebeispiele sind ausreichend aber oft nur Skizzen. Es fehlt häufig die Einbettung in einen Anwendungskontext, wie sie z.B. bei der Matrizenmultiplikation gegeben ist. Oft wird auf die API-Dokumentation verwiesen, die man tatsächlich auch braucht, will man das alles verstehen. Das Kapitel ist eher eine Sammlung von Methoden und hat für mich keinen richtigen roten Faden.

 

Dafür ist hier die Abstimmung mit den Einsendeaufgaben sehr gut. Die laufen meist darauf hinaus, etwas zu programmieren, bei dem die vorgestellten Klassen zum Einsatz kommen. Oft reicht am Ende wenig Code. Trotzdem habe ich hier regelmäßig gemerkt, was ich eben noch nicht verstanden hatte. Ich musste mich regelmäßig durch die API-Dokumentation wühlen, um Erfolg zu haben. Rückblickend meine ich, dass das mehr gebracht hat als ein Text, der von vorneherein alle Eventualitäten erklärt. Das Lehrbuch lässt genug offen, um in den Aufgaben Spielraum für eigene Erkundungen und Aha-Erlebnisse zu haben. Im Zusammenspiel mit den Aufgaben funktioniert das Buch sehr gut.

 

Im nächsten Kapitel geht es um fortgeschrittene Konzepte von Nebenläufigkeit in Java. Hier geht es z.B. um:
- das kontrollierte Beenden von Threads
- Swing-GUIs und Nebenläufigkeit: Fallbeispiel ist hier die Fortschrittsanzeige eines nebenläufigen Auftrages. Eine alltägliche Situation, die komplizierter ist als man zunächst denkt.

- Caches und Synchronisation

 

Ans Eingemachte geht es mit den Abschnitten über Caches und Synchronisation. In modernen PCs haben die CPUs ja mehrstufige Caches, in denen sie oft benötige Daten ablegen. Arbeiten mehrere CPUs parallel, stehen im Cache eventuell Daten, die im RAM schon längst geändert wurden. Eine gemeinsam genutzte Variable kann so aus Sicht verschiedener CPUs oder Kerne zugleich verschiedene Werte haben, was dem intuitiven Verständnis von Speicherinhalt widerspricht. So etwas kann zu bizarren und schwer nachvollziehbaren Bugs führen. Java bietet Sprachkonstrukte, mit denen man einen Refresh oder Flush des Caches erzwingen kann. Diese sollte man aber nicht ständig einsetzen, weil so die Zugriffszeiten auf Speicherinhalte steigen und das Programm langsamer wird. Man kommt also nicht umhin, sich zu überlegen, was man tut. Auch in diesem Kapitel war die Verzahnung mit den Einsendeaufgaben sehr gut.

 

Verteilte Programmierung

 

Im letzten Kapitel geht es dann um verteilte Anwendungen mit RMI (Remote Methode Invocation). Eine Java-Anwendung kann auf mehrere Rechner verteilt sein, z.B. auf einen Client und einen Server. Ein Client kann Methoden auf dem Server aufrufen. Dazu erhält er über das Netz sogenannte Stummel-Objekte, die sozusagen nur die Schnittstelle und keine Implementierung enthalten. Arbeitet man mit RMI, so implementiert man viele Dinge etwas anders als bei einer Anwendung, die lokal läuft. Entfernte Methodenaufrufe dauern relativ lange. Darum bieten sich Methoden an, die z.B. mehrere Attribute eines Objektes auf einmal abfragen oder verändern.

 

Es war  schwierig, zum Thema RMI gute und aktuelle Quellen neben dem Kursmaterial zu finden. Manche Codebeispiele liefen nicht mehr so wie im Buch beschrieben, weil inzwischen verschärfte Sicherheitskonzepte gelten. Ich habe hier nicht alles ausprobieren können. Verteilte Programmierung ist komplex. Dies gilt auch für die Verteilung und Installation von Anwendungen. Ich fand es gut, dass dieses Thema behandelt wurde, merkte aber an dem Punkt auch, dass es Grenzen gibt, wie viel Stoff ich für eine Modulprüfung verdauen kann.

 

Zum Glück wies mein Tutor mich darauf hin, dass dieses letzte Kapitel nicht klausurrelevant ist. Für den Online-Test und die Online-Klausur ist es aber wichtig, also sollte man sich gut damit beschäftigen.

 

Online-Tests und Online-Klausur

 

Bei den Tests gab es in diesem Kurs eine Besonderheit. Es waren vergleichweise wenige und zu mehreren Kapiteln gab es gar keine. Der Abschluss-Test deckte aber auch diese Kapitel ab. Hier musste man also direkt liefern ohne zuvor an Fragen zum gleichen Stoff sein Verständnis überprüfen zu können. Es ist mein Eindruck, dass hier bewusst noch eine kleine Hürde vor den ersehnten Bonuspunkten eingezogen wurde. Tatsächlich habe ich bei diesem Kurs nur vergleichsweise wenige Bonuspunkte erringen können, sowohl im Online-Test als auch in der Online-Klausur, die ich ebenfalls als herausfordernd empfand. Der eigentliche Nutzen der Online-Klausur war denn auch das Feedback meines Tutors, das mir ermöglicht hat, die Aufgaben noch einmal zu überarbeiten und dabei noch einige Einsichten zu erlangen. Auch in der Online-Klausur kann Stoff aus dem gesamten Kurs drankommen, auch zu dem Kapitel über RMI.

 

Präsenzklausur

 

Für die Präsenzklausur habe ich das Lehrbuch insgesamt 3 mal durchgearbeitet und mit vielen Anmerkungen versehen. Außerdem habe ich mir zum Thema viel Sekundärliteratur aus der Unibibliothek besorgt. Einen konkreten Titel nennen möchte ich aber nicht, weil ich in den meisten Büchern immer nur kurze hilfreiche Abschnitte gefunden habe. Das Lehrbuch von Ziesche und Arinir ist schon sehr gut zusammengestellt und behandelt sehr viele Konzepte, die man anderswo weit verstreut zusammensuchen muss. So kam ich letztlich doch immer wieder darauf zurück. Ich würde für die Klausur empfehlen, die Kapitel 2 bis 5 gründlich zu wiederholen.

 

Die Präsenzklausur war breit angelegt. Ein Mix aus Wissensfragen, Code analysieren, Code schreiben, UML-Diagramme zeichnen. Inhaltlich wurden eher die Grundkonzepte abgeprüft und nicht so sehr ausgefallene Klassen aus java.util.concurrent. Ich würde empfehlen, die theoretischen Grundlagen und die Kapitel zur UML gut zu wiederholen. Und die grundlegenden Kapitel zu Threads in Java. Damit hat man schon mal einen guten Teil abgedeckt. Strategisch würde ich bei dieser Klausur dazu raten, erst die leichteren Aufgaben mit weniger Punkten zu machen und erst dann die umfangreicheren. Ich habe es umgekehrt gemacht, aber das war hier klar die falsche Strategie, weil ich an irgendeinem Punkt abbrechen musste und später nur schwer wieder reinkam. Eine Aufgabe war etwas anspruchsvoller, weil ein Thema, dass im Kurs nur recht knapp vorgestellt wurde, nun in einer Anwendungssituation ausprogrammiert werden sollte. Mein Tipp: Wenn im Kurstext die Rede davon ist, sich gewisse Dinge noch einmal in der API anzuschauen, dann macht das. Kann auch nicht schaden, die knappen Codebeispiele abzutippen, auszuführen und selbst ein bisschen damit zu experimentieren. Das bezieht sich vor allem auf Kapitel 5.

 

Fazit

 

Insgesamt wieder ein schöner Kurs. Leider ist damit die Serie der Java-Module nun vorbei. Schade, denn gerade die haben mir besonders viel Spaß gemacht. Es wird noch ein Wiedersehen mit Java geben, weil auf mich noch der Kurs "Java Server Pages" aus dem Fachgebiet "Web- und Medieninformatik" wartet. Da geht es dann um serverseitige Anwendungen, die dynamisch HTML erzeugen. Das macht man meistens mit PHP aber bei Springer Campus eben mit Java.

 

Nachtrag (08.12.2016)

 

Heute kam das Ergebnis der Präsenzklausur. Ich hatte Glück und habe ziemlich gut abgeschnitten. Hier konnte ich wieder einmal lernen, dass ich mich nicht entmutigen lassen sollte, auch wenn der Stoff eines Moduls am Anfang schwer zu fassen ist. Nichtsequentielle Programmierung habe ich als das schwierigste der Java-Module empfunden. Ich habe hier ein paar Anläufe mehr gebraucht, um die Konzepte wirklich zu verstehen. Gerade die Online-Klausur, bei der noch vieles schief gelaufen ist, hat mir hier noch einmal wertvolle Rückmeldungen gebracht. Auch weil mein Tutor sich nicht damit begnügt hat, meine Arbeit nur mit Punkten zu bewerten sondern differenzierte inhaltliche Rückmeldungen gegeben hat, wo genau die Mängel meiner vorgelegten Lösungen waren. Das hat mir erlaubt, mich "auf den letzten Metern vor dem Ziel" noch einmal zu steigern.

 

Ich denke, dass das ganze auch ein bisschen mit seelischer Reife zu tun hat. Wenn ich an mein Erststudium zurückdenke, so meine ich, dass ich mich damals von Schwierigkeiten viel schneller habe verunsichern lassen. Ich bin jetzt ein paar Jahre älter und habe in meinem Leben auch den einen oder anderen Misserfolg wegstecken müssen, privat wie beruflich. Da erlebt man, dass man nach einen Sturz auch wieder aufstehen kann. Dass Fehler vor allem den aktuellen Lernstand wiederspiegeln, aber eben längst nicht das eigene Potential für die Zukunft. Natürlich hatte ich das auch mit 19 Jahren schon einmal gehört. Aber jetzt ist das eigene Erfahrung und nicht der gute Rat anderer. Es macht viel mehr Spaß als früher, mit dieser veränderten Perspektive zu studieren.

2 Kommentare


Empfohlene Kommentare

Zitat

Für die Präsenzklausur habe ich das Lehrbuch insgesamt 3 mal durchgearbeitet 

 

Hast du dabei ein bestimmtes Schema, nach dem du vorgehst?

Link zu diesem Kommentar

Nein. Das war eher dem Gefühl geschuldet, dass es noch nicht reichen würde.

 

Ich hatte beim ersten Durcharbeiten zunächst den Eindruck, die verschiedenen Konzepte zu verstehen. Beim Bearbeiten der Einsendeaufgaben habe ich aber immer wieder gemerkt, dass mein Verständnis doch noch zu oberflächlich war. Außerdem fiel es mir schwerer als bei anderen Modulen, die Themen im Gedächtnis zu behalten. Ich musste immer wieder zurückblättern und Dinge noch mal nachschlagen. Das machte mir Sorge im Hinblick auf die Klausur.

 

Das Lehrbuch ging Nebenläufigkeit ja aus verschiedenen Blickwinkeln an. Erst Theorie und Konzepte. Dann Umsetzung in Java. Dann Modellierung in UML. Später im Modul noch mal Java-Sprachkonstrukte, die die Implementierung z.B. von eigenen ausgefalleneren Monitor-Konstrukten ermöglichen.

 

Beim zweiten Lesen habe ich gemerkt, dass ich die theoretischen Konzepte besser verstand, weil ich nun wusste, wie die in Java umgesetzt werden. Und natürlich weil ich darauf bezogene Aufgaben gelöst hatte. Umgekehrt habe ich die Implementierungen in Java wieder besser verstanden, weil mir die theoretischen Hintergründe klarer waren. Ich hatte dann auch mehr eigene Ideen, wie man den Code verändern kann. Beim zweiten Lesen habe ich mir viele Anmerkungen ins Buch geschrieben. Das mache ich normalerweise schon beim ersten Mal, aber diesmal hatte ich beim ersten Lesen noch nicht das Gefühl, ganze Absätze treffend auf eine kurze Aussage verdichten zu können.

 

Erst beim dritten Durchgang und auch mit den Rückmeldungen aus der Online-Klausur hatte ich das Gefühl, jetzt wird es leichter. Ich habe die Aufgaben aus der Online-Klausur noch mal überarbeitet. Das war auch toll von meinem Tutor, dass er da noch mal drüber geschaut hat und mir Rückmeldungen gegeben hat.  Offiziell endet die tutorielle Betreuung nämlich mit der Korrektur der Online-Klausur.

Bearbeitet von kurtchen
Link zu diesem Kommentar

Erstelle ein Benutzerkonto oder melde Dich an, um zu kommentieren

Du musst ein Benutzerkonto haben, um einen Kommentar verfassen zu können

Benutzerkonto erstellen

Neues Benutzerkonto für unsere Community erstellen. Es ist einfach!

Neues Benutzerkonto erstellen

Anmelden

Du hast bereits ein Benutzerkonto? Melde Dich hier an.

Jetzt anmelden


×
  • Neu erstellen...