Lösungen zu Blatt 2 - KIT

Werbung
Vorlesung Verteilte Systeme
Übungsblatt 2
Aufgabe 1:
Beschreiben Sie (z.B. mit Pseudocode) die zwei grundsätzlichen Möglichkeiten, wie man in
Java Threads erzeugt.
Aufgabe 2:
Welche der folgenden Aussagen sind wahr oder falsch?
a) Der schreibende Zugriff auf Objektattribute einer Klasse von verschiedenen Threads aus
ist immer unkritisch.
b) Der schreibende Zugriff auf lokale Variablen einer Klassenmethode von verschiedenen
Threads aus ist immer unkritisch.
c) Der schreibende Zugriff auf Klassenattribute (static-Attribute einer Klasse) von
verschiedenen Threads aus ist immer kritisch.
d) Wenn ein Thread in einem wait() auf etwas wartet, kann dieser Wartezustand nicht
durch Schicken eines „Interrupts“ (per Aufruf der interrupt()-Methode) unterbrochen
werden.
e) Der Aufruf von notify() auf einem Datenobjekt, an dem Threads warten, weckt alle
wartenden Threads auf.
f) Wenn nur ein Thread schreibend auf eine Resource zugreift und die anderen nur lesen,
dann muss der Zugriff nicht synchronisiert werden.
g) Der Aufruf der Methode interrupt() auf einem Threadobjekt wirft eine
InterruptedException, wenn dieser Thread auf etwas wartet (z.B. in einem wait()Aufruf).
h) Der Aufruf der Methode interrupt() auf einem Threadobjekt wirft auch dann eine
Interrupted-Exception, wenn der Thread nicht auf etwas wartet.
i) Wenn zwei Threads nacheinander auf verschiedene Objekte als gemeinsame Ressourcen
schreibend zugreifen, dann sollten die Objekte von beiden Threads immer in der
gleichen Reihenfolge gesperrt werden, um Deadlocks zu vermeiden.
j) Eine Java-Klasse hat zwei mit synchronized verriegelte Methoden m1() und m2(). Man
sollte nie Methode m1() in der Methode m2() oder umgekehrt aufrufen, sonst erhält man
einen Deadlock.
Aufgabe 3:
Warum sollte man in Java und auch in anderen Sprachen einen Thread nie durch Aufruf
einer Art stop()-Methode von einem anderen Thread aus unmittelbar beenden können, bzw.
warum ist diese Methode in Java „deprecated“. Wie sollte man dann Threads beenden?
Aufgabe 4: (Programmieraufgabe)
Erläutern Sie am Beispiel (z.B. das Threadbeispiel aus der Vorlesung), was
Nichtdeterminismus einer Verteilten Anwendung ist.
Aufgabe 5: (Programmieraufgabe)
Schreiben Sie den Serverteil der Wetteranwendung so um, dass er einen Threadpool von
Threads zur Verarbeitung der empfangenen Datenpakete auf der Serverseite verwendet.
Verwenden sie hierfür den ExecutorService des java.util.concurrent-Paketes.
Vorlesung Verteilte Systeme
Musterlösungen zu Übungsblatt 2
Aufgabe 1:
Lösungsvariante 1 ist, eine eigene Klasse von der Klasse Thread abzuleiten und dann zu
instanziieren.
public class MyThread extends Thread {
public void run() {
// do something
}
}
MyThread thread = new MyThread();
thread.start();
Die zweite Lösung ist, eine Klasse zu erstellen, die das Interface Runnable implementiert,
und dann ein Thread-Objekt mit einem Objekt dieser Klasse als Argument im Konstruktor
zu erzeugen.
public calls MyRunnable implements Runnable {
public void run() {
// something to run
}
}
Thread thread = new Thread(new MyRunnable());
thread.start();
Aufgabe 2:
a) Falsch (wenn mehrere Threads auf das gleiche Objekt zugreifen, muss der Zugriff auf
Objektattribute verriegelt werden)
b) Wahr
c) Wahr (Hier greifen all eThreads bei Benutzung auf die selben Attribute zu)
d) Falsch (interrupt() unterbricht gerade den Schlafzustand bei wait())
e) Falsch (Es wird nur ein einzelner Thread aufgeweckt; sonst notifyAll() verwenden)
f) Falsch (auch bei nur einem Writer muss man verriegeln)
g) Wahr
h) Falsch (Die Exception wird nur geworfen, wenn sich der Thread in einem Wartezustand
befindet, siehe (g))
i) Wahr
j) Falsch (Ein Objekt blockiert sich nie selbst an einem von ihm gehaltenen Lock)
Aufgabe 3:
Da ein Master- oder Kontroll-Thread nicht wissen kann, was seine Worker-Threads gerade
tun, sollte man diese auch nicht einfach mit einer Art stop()-Methode beenden können, ohne
dass sie Zeit haben, ihre augenblickliche Arbeit in einen für das Programm sicheren Status
zu überführen (z.B. geöffnete Ressourcen aufzuräumen oder eine Operation zum Eintragen
von Daten in eine Datenbank auch vollständig zu beenden). Dies wird typischer Weise
durch ein Benachrichtigungskonzept (Signalkonzept) erreicht, bei dem den zu beendenden
Threads eine Nachricht (Signal) übermittelt wird, damit sich diese nach Empfang der
Nachricht selbst beenden können (nachdem sie ihren Zustand in einen geeigneten Zustand
gebracht haben). Hierfür dienen in Java die Methoden interrupt() (Senden der Nachricht)
sowie die InterrruptedException und isInterrupted() Methoden (Empfang der Nachricht).
Aufgabe 4:
Im Beispiel der Vorlesung für Threaderzeugung haben die in der Main-Methode erzeugten
Threads in einer Schleife über den Aufruf von System.out.println() Ausgaben auf der
Konsole gemacht. Wenn man das Programm verschiedene Male startet, sieht die
Reihenfolge der Ausgabe dabei immer anders aus. Sie scheint einem Betrachter von außen
als zufällig, da sie nicht vom Programm sondern anderen Faktoren, wie das Scheduling des
Betriebssytems oder der Java-Virtualmaschine abhängt, die für den Betrachter nicht
nachvollziehbar sind. Dieses scheinbar-willkürliche Ineinanderverchachtelung der Ausgaben
nennt man daher nicht-deterministisches (Nicht-vorhersehbares) Verhalten der Anwendung.
Aufgabe 5: (10 Pkte)
Das Programmbeispiel (siehe Sourcecode auf der Website) wurde mit Hilfe der
ExecutorService-Klasse des java.util.concurrent-Packages unter Verwendung eines WorkerThreadPools mit fester Größe (Methode newFixedThreadPool()) auf der Serverseite
parallelisiert. Der Code zum Eintragen der Daten in die Datenbank wurde dabei in eine
Klasse DataProcessor ausgelagert, die das Runnable-Interface implementiert, in der die
Arbeit zum Eintragen der Daten in die Datenbank gebündelt sind. Da jetzt von parallelen
Threads auf die Datenbank zugegriffen wird, darf man nicht mehr mit einem gemeinsam
genutzten Connection-Objekt arbeiten, da die Klasse Connection nicht Thread-Safe ist.
Stattdessen wird ein Connection-Pool (auf Basis des c3p0-Frameworks) verwendet, der den
Threads bei Bedarf eine Connection explizit für diesen Thread bereitstellt. Diese exklusive
Connection wird dann mit Aufruf der close()-Methode an den Connection-Pool
zurückgegeben, wenn sie nicht mehr gebraucht wird.
Das Programm enthält auch Code, der den Thread-Pool terminiert und den Datagram-Socket
schließt, wenn das Serverprogramm von der Konsole aus beendet wird.
Herunterladen