Vorlesungsfolien 5. Dezember

Werbung
Paradigmen der Programmierung
Nebenläufigkeit
Prof. Dr. Christian Kohls
Informatik, Soziotechnische Systeme
2. Threadsicherheit
• Primitive Thread-Operationen in Java
• Parallelität in Berechnungen
• Probleme mit Wettlaufbedingungen
• Threadsicherheit:
• Unveränderliche Objekte
• Atomare Operationen
• Möglichst wenig gemeinsame Variablen
• Threadlokale Variablen
• Objektsperren
Nebenläufige Programmierung
Ungewohnte Programmierung, aufgrund von
- Nichtdeterminierten Abläufen
- Wettlaufbedingungen
- Möglichen Deadlocks
- Unerwarteten Effekten aufgrund von Compileroptimierungen
 Viele Probleme, die sich aber reduzieren lassen durch
– Funktionale Programmierung (=> Zustandslosigkeit)
– Botschaftenaustausch (=> Weniger Wettbewerbssituationen)
– Frameworks und Bibliotheken (Optimiertes Design)
•
•
•
•
Aus Effizienzgründen sind oft besondere Lösungen nötig.
Primitivoperationen können auch die Grundlage für Frameworks sein.
Primitivoperationen sind fehleranfälliger!
Man kann sie aber beherrschen, wenn man sich auskennt!
Threads mit Java
Threadausführung und die Elemente eines Java-Programms
Die Ausführung eines Java-Programms umfasst:
• Klassen zur Beschreibung von Objekten
• Objekte zum Speichern von Information
• Methoden, die sagen, wie etwas gemacht wird
• Einen Thread der sich durch die Methoden durchschlängelt und jeweils
genau eine Methode ausführt. Der Main-Thread startet mit der Funktion
main()
• Eventuell weitere Threads (automatisch oder programmiert)
(Multithreading)
• Verschiedene Speicherbereiche (Stack, Heap, Register, Cache)
• Den Prozess der virtuellen Maschine in dem sich das alles abspielt
Java ist so entworfen, dass man architekturunabhängig korrekte
Multithreading-Programme schreiben kann – aber das ist nicht einfach!!
Automatische Threads in Java
Unabhängig von besonderen Anweisungen des
Programms enthalten Java-Programme mehrere Threads:
• main-Thread, der das eigene Programm ausführt.
• Threads für die automatische Speicherbereinigung.
• Event-Thread für die Behandlung der
Benutzerinteraktion.
Diese automatischen Threads stellen (in einfachen
Programmen) keine Probleme dar.
Explizites Erzeugen von Threads
•
•
Java-Threads müssen direkt oder indirekt die Schnittstelle java.lang.Runnable
implementieren und damit über eine Methode run() verfügen.
Die Methode run() wird beim Start des Thread aufgerufen. Sie stellt praktisch die
jeweilige main-Funktion dar.
package java.lang;
public interface Runnable {
public void run();
}
Um einen Thread zu erzeugen, benötigt man die Hilfe der Klasse Thread, die
ebenfalls bereits die Schnittstelle Runnable implementiert.
Es ergeben sich unterschiedliche Möglichkeiten Threads zu erzeugen:
• Statische Erweiterung der Klasse Thread durch Vererbung
• Dynamische Erweiterung der Klasse Thread durch Delegation (besser)
• Erzeugen von Threads durch anonyme Klassen (Funktionsobjekt)
Runnable interface
Delegation
Lebenszyklus
• run() enthält Anweisungfolge für den Thread – wie main()
• Thread erzeugen:
Thread-Objekt existiert, aber ist noch nicht Ablaufbereit
• start() aufrufen: Thread ist ausführbar
• Achtung: start() und nicht run() aufrufen!
• Thread endet wenn
– Code in run() abgearbeitet ist
– Thread als Daemon arbeitet und keine anderen Threads mehr laufen
• stop() ist deprecated und nicht sicher
• Andere Threads können interrupts senden, aber dies dient nur als
„Hinweis“ – Thread muss sich selbst stoppen, d.h. Code beenden
Die Klasse Thread
class Thread {
static Thread currentThread()
void run()
void start()
static void sleep(long millis)
static void yield()
Thread.State getState()
boolean isAlive()
boolean isInterruped()
join()
setDaemon(boolean b)
void setPriority(int new Priority)
...
Vereinfachtes Zustandsdiagramm der Threadausführung
lock
wait()
sleep()
unlock
notify()
start()
Scheduler
Ablauf
fertig
interrupt()
NEW: Neuer Thread, nicht gestartet
RUNNABLE: Läuft in der JVM wenn Rechenzeit zugeteilt
BLOCKED: Wartet auf das Eintreten in einen kritischen Bereich (Monitore)
WAITING: wartet auf notify()
TIMED_WATING: nach sleep()
TERMINATE: Beendet
Interrupts
Interrupts und InterruptedException
Fehlerhaftes aktives Warten
(busy waiting – schlecht!!)
Die einfachste Idee ist, in einer while-Schleife immer wieder die
Bedingung abzufragen:
Das vergeudet Prozessorzeit und ist falsch:
while (q.isEmpty()) /* nichts */ ;
Das ist falsch:
while (q.isEmpty()) Thread.yield() ;
Auch das ist falsch:
while (q.isEmpty()) Thread.sleep(delay) ;
Die meisten Anwendungen des aktiven Wartens vergeuden
Prozessorzeit. Sie sind zudem falsch, wenn nicht auf die Sichtbarkeit
der Bedingungen geachtet wird.
Monitor-Mechanismen für passives Warten
Alle nötige Funktionalität wird von der Klasse Object geerbt. Daher kann jedes
Objekt für eine Wartebedingung stehen.
Allerdings muss die ausführende Thread zum Zeitpunkt des Aufrufs dieser Methoden
über die Sperre des Objekts verfügen (wird zur Laufzeit überprüft).
obj.wait();
Warte und gib die Sperre frei.
Wenn der Thread ein notify()-Signal erhält, versucht er die Sperre wieder zu
bekommen (kann was dauern) und macht dann weiter.
obj.wait(long msecs);
Warte maximal msecs Millisekunden.
wait() kann von außen unterbrochen werden: InterruptedException.
obj.notify();
Sende einem auf obj wartenden Thread ein notify()-Signal.
obj.notifyAll();
Sende allen auf obj wartenden Threads ein notify()-Signal.
Hinweis: Später erfahren Sie mehr über Sperren und Monitore.
Möglicheiten & Grenzen der Parallelverarbeitung
1
2
3
4
1
2
3
4
1
4
9
16
1
4
9
16
Unabhängige Daten!!!
Summe berechnen
1
2
3
4
7
3
10
Kumulierte Werte in Array eintragen
1
2
3
4
Kumulierte Werte in Array eintragen
1
1
2
3
4
Kumulierte Werte in Array eintragen
1
2
1
3
3
4
Kumulierte Werte in Array eintragen
1
2
3
1
3
6
4
Kumulierte Werte in Array eintragen
1
2
3
4
1
3
6
10
Werte setzen
1
1
1
1
2
2
2
2
2
2
2
2
Werte setzen
1
1
1
1
1
2
1
1
1
2
2
2
Werte setzen
1
1
1
1
1
2
1
2
2
2
2
2
Alle inkrementieren
1
2
3
4
5
2
3
4
2
3
4
5
Alle inkrementieren
2
2
3
4
5
3
4
5
2
3
4
5
• Was passiert eigentlich beim
gleichzeitigen Setzen?
• Was ist die Ursache für das Problem?
• Wie ist dies beim Actor-Ansatz gelöst?
• Welche Probleme bleiben?
Zeitliche Unbestimmtheit
• Ausführungsreihenfolge innerhalb eines Threads ist
sequentiell und deterministisch
• Ausführungsreihenfolge zwischen mehreren Threasd
ist nicht deterministisch
-> Wettlaufbedingungen
• Nicht-determiniert bedeutet:
– Abläufe können unterschiedlich sein
– Fehler treten nur manchmal auf!
– Ausführungsreihenfolge lässt sich nicht beobachten
(da jedes Mal anders)
• Bestimmte Operationen müssen als atomare Einheit ausgeführt
werden, um die Klasseninvarianz zu erhalten. Mehrere Threads
können aber unabhängig voneinander gemeinsame Daten
verändern.
Mehrere Threads können auf dem gleichen Objekt ausgeführt werden
Thread 1
Anweisung 1
Anweisung 2
Obj1.fktAlpha
Anweisung 4
Anweisung 5
Anweisung 6
Anweisung 7
Anweisung 8
Anweisung 9
Obj2.fktBeta
Anweisung 11
…
Objekt 1
fktAlpha() {
Anweisung x
Anweisung y
}
Objekt 2
fktBeta() {
Anweisung a
Anweisung b
}
Thread 2
Anweisung 1
Obj1.fktAlpha
Anweisung 2
Anweisung 3
…
Optimierungen
Der Compiler, die Java-Laufzeitumgebung und der
Prozessor können innerhalb dieser Grenzen
Modifikationen vornehmen um die Effizienz zu steigern.
Und das tun sie auch!!!
– Soweit es keinen Einfluss auf die Ergebnisse hat, kann die Reihenfolge
von Befehlen verändert werden (reordering).
– Daten können zeitweise in Registern und Cache-Speichern gehalten
werden. In dieser Zeit ist der Inhalt des Hauptspeichers nicht korrekt. Es
ist denkbar, dass ein Objekte niemals im Hauptspeicher erscheint
(visibility) .
Im Ergebnis kann erst durch diese Maßnahmen die Leistungsfähigkeit
moderner Computer erreicht werden!
Diese Punkte sind vielen Java-Entwicklern nicht bekannt!
Folge: Programmfehler !!!
Wettlaufbedingungen
(Race Conditions)
Probleme entstehen, sobald mehrerer Threads auf gemeinsame
Variable zugreifen. Dies kommt daher, dass sowohl die
Ausführungsumgebung (vom Compiler bis zur CPU) als auch die
logische Sicht der Objektorientierung zunächst nur für den
sequentiellen Ablauf in einem Thread ausgelegt sind.
•
Problem des gleichzeitigen Schreibens
•
Unbestimmte Reihenfolge gesetzter oder verarbeiteter Werte
(Nichtdeterminiertes Timing)
•
Read-Modify-Write
•
Check-then-act
 Verletzung der Invarianz
Wettlaufbedingungen und Invarianten
In Bezug auf Korrektheit wurden im 2. Semester wichtige Grundregeln
formuliert:
1. Man kann die Korrektheit einer Methode überprüfen, indem man nachvollzieht,
welche Anweisungen in der Methode ausgeführt werden und welche
Auswirkungen die aufgerufenen Methoden haben.
2. Zu einer Klasse gehören Invarianten, d.h. Aussagen über die erlaubten Werte
der Instanzvariablen, die beim Eintritt und beim Verlassen einer Methode erfüllt
sein müssen.
•
•
Die Verwendung dieser Regeln ermöglicht es, Methoden unabhängig
voneinander zu entwickeln.
Wenn es möglich ist, dass während der Ausführung einer Methode
(innerhalb eines Thread) ein anderer Thread eine Methode desselben
Objekts aufruft, ist die Korrektheit nicht mehr garantiert.
ZIEL:
Daten vor unkontrolliertem gleichzeitigen Zugriff schützen, Invarianz erhalten
Thread-Sicherheit (thread safety)
Ein Objekt ist threadsicher wenn es korrekt funktioniert und von anderen
Objekten immer in einem gültigen Zustand gesehen wird auch wenn mehrere
Threads darauf zugreifen. Dies muss unabhängig vom Scheduling und der
nicht-determinierten Ablaufreihenfolge gelten.
Aufrufender Client-Code muss keine zusätzlichen Synchronisationsmaßnahmen
treffen threadsicherheit zu wahren.
Thread-Sicherheit (thread safety)
•
Der Zustand eines Objekts ist gegeben durch die Werte seiner
Instanzvariablen!
•
Klassenvariable gehören zum Klassen“objekt“. Lokale Variable, gehören zu
einem Thread (und sind damit unproblematisch).
•
Jedes Objekt sollte von außen stets in einem gültigen Zustand angetroffen
werden.
•
Aber nicht jedes Objekt sollte threadsicher programmiert sein, da damit
beträchtlicher Laufzeitoverhead verbunden ist.
•
Für jedes Objekt sollte bekannt sein, ob es threadsicher ist oder nicht.
•
Unveränderliche Objekte (z.B. Strings) oder zustandslose Objekte sind
automatisch threadsicher!
•
Es lässt sich nicht vermeiden, dass während der Ausführungszeit einer
öffentlichen Methode ein Objekt zeitweise den gültigen Zustand verlässt.
Threadsicherheit bedeutet, dass während dieser „kritischen“ Zeit der Zugriff
durch andere Threads verhindert wird.
Maßnahmen zum Gewährleisten der Threadsicherheit
• Möglichst wenig gemeinsame Daten
– Objekte nur in einem Thread benutzen (confinement/Kapselung)
– Threadlokale Variable nutzen
• Unveränderliche Objekte
– Objekte dürfen erst nach der Konstruktion bekannt werden
– Konstante statt variable Werte
•
•
•
•
•
•
Atomare Operationen nutzen (Bibliothek)
Gemeinsame Variable als volatile deklarieren
Kommunikation über sichere Bibliotheksklassen
Minimieren der Interaktion von Threads durch einfache Architektur!
Dokumentation von Strategien und Policies
Zugriff zu unsicheren Objekten durch Objektsperren sichern
Merke:
Immer wenn mehr als ein Thread auf eine veränderbare Variable zugreifen,
und mind. ein Thread diese verändert, dann müssen alle ihren Zugriff durch
Synchronisation koordinieren.
Maßnahme 1: Kapselung
Welche Variablen sind lokal zu einem Thread, welche gemeinsam?
In Java haben wir:
•
Lokale Variable und Funktionsparameter: Diese Variablen liegen auf dem Stack. Sie sind nur
innerhalb der laufenden Methode und (damit) auch immer nur in einem Thread sichtbar.
•
Threadlokale Variable (Klasse java.lang.ThreadLocal) sind nur in einem Thread sichtbar.
•
Objekte und ihre Inhalte sind im Heap gespeichert. Sie können von allen Threads angesprochen
werden.
•
Klassenvariable verhalten sich wie die Inhalte von Objekten.
•
Die Probleme der Nebenläufigkeit haben nur mit
dem Zugriff auf gemeinsame Variable zu tun!
•
Fehler sind durch Testen kaum zu erkennen. Daher kommt es darauf an, einfach und sicher
zu programmieren!
•
Just as encapsulation can make it easier to preserve invariants,
local variables can makte it easier to confine objects to thread.
Maßnahme 2: Unveränderliche Daten
•
•
•
Unveränderliche Objekte sind absolut unkritische Behälter für Werte.
Wann immer möglich Variablen als final deklarieren
Ggf. deep copy von Containern
•
•
Ein Objekt ist unveränderlich, wenn alle seine Komponenten unveränderlich sind und
wenn alle Variablen final sind. Unveränderliche Objekte sind threadsicher.
@Immutable
public class Point{
public final double x;
public final double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
}
Es ist bei einfachen Daten-Objekten auch nicht nötig, dass die Variablen gekapselt sind (solange
keine Implementierungsgeheimnisse im Spiel sind).
Unsichere Objektkonstruktion
•
register() macht das Objekt vor der Fertigstellung bekannt. Damit kann z.B. ein falscher Wert für die
Konstante gefunden werden.
•
Ebenso wird durch das Starten des Threads bewirkt, dass mit einem unvollständig initialisierten Objekt
gearbeitet werden kann.
•
Fazit: Niemals einen Thread im Konstruktor starten. Die this-Referenz nicht anderen Objekten vom
Konstruktor aus bekannt machen.
Warnung: Die Reihenfolge ist nicht allein schuld! Konstanten gelten erst nach Ablauf des Konstruktors
als „eingefroren“.
•
Problematisches „lazy“ Singleton-Muster
Maßnahme 3: Volatile
•
•
•
•
•
Allgemein gibt es keine Garantie, dass die Änderungen durch einen Thread für andere Threads
sichtbar werden (Caching!)
Synchroinsation für sichtbarkeit zwischen Threads erforderlich
Änderungen an volatile-Variablen sind immer in der richtigen Reihenfolge sichtbar und Variablen
werden nicht gecached.
Zusätzlich wird ab Java 5 auch garantiert, nach dem Zugriff auf eine volatile-Variable auch davor
stattgefunden Veränderungen durch den ändernden Thread sichtbar sind.
Volatile garantiert auch, dass 64 bit Werte (double und long) atomar verändert werden.
Maßnahme 4: Atomare Operationen
•
Atomares Test & Set: Atomare Test & Set – Operationen bilden den Kern
für die Implementierung aller effizienten Mechanismen der Nebenläufigkeit.
Auch der Zähler lässt sich so realisieren. Es bietet aber auch die
Möglichkeit für die Implementierung anderer Operationen
compareAndSet prüft, ob der aktuelle Wert gleich oldCount ist und setzt ihn
genau dann auf den neuen Wert. Die Rückgabe informiert über den Erfolg der
Aktion, die atomar abläuft.
Verwendung von atomaren Referenzen für
veränderliche Objekte
Maßnahme 5: Objektsperren
•
Sperren stellen sicher, dass auf einer Ressource höchstens eine Aktivität
stattfindet.
•
In Java werden Sperren meist über Monitore abgebildet
•
Es gibt einen wechselseitigen Ausschluss
•
Wenn ein Thread sich den Zugriff auf die Ressource gesichert hat, können
keine anderen Objekte daraufzugreifen – sie müssen warten.
•
Derart geschützte Aktivitäten werden als kritischer Abschnitt bezeichnet.
•
•
•
Objektsperren sind manchmal erforderlich, um Invarianz zu garantierten.
Aber: Verlust der Nebenläufigkeit (Blockieren, Warten)
Aber: Gefahr des Deadlocks (Programm läuft nicht weiter)
Implementierung von Sperren
•
Kritische Abschnitte können über Bibliotheksmechanismen geschützt werden. Dabei wird zu
Beginn des Bereichs die Methode lock() und am Ende unlock() aufgerufen.
•
Alternativ kann ein Block (oder eine Methode) gemäß dem Monitorkonzept von Java durch
synchronized { ... } gesichert werden.
•
In beiden Fällen verfügt ein Objekt über eine Sperre (lock) (beim Monitorkonzept kann das jedes
beliebige Objekt sein), die den Zugang zu dem kritschen Bereich steuert.
•
Ein Thread, der einen ungesperrten Bereich betritt, erhält die Sperre für das zugehörige Objekt. Er
darf damit alle gesamten geschützten Bereiche des Objekts betreten (auch wenn diese über
mehrere Methoden verteilt sind).
•
Wenn er alle geschützten Bereiche verlassen hat, gibt er die Sperre wieder frei.
•
Diese Eigenschaft heißt auch Wiedereintrittsfähigkeit (reentrancy).
•
Ein Thread, der versucht, einen gesperrten Bereich zu betreten, wartet bis der Bereich frei wird
und er die Sperre erhält.
•
Die Sperre verwaltet eine Liste von wartenden Threads (entry-set). Beim Freiwerden der Sperre
wird einer der wartenden Threads ausführungsbereit.
Schutz des kritischen Bereichs (Zähler-Beispiel)
Besonderheiten der Objektsperre
• Eine Sperre schützt kritische Bereiche und dazugehörende Variable
(Invariante!).
• Zusammengehörende Bereiche und Variablen müssen mit der
gleichen Sperre geschützt werden.
• Fremde Bereiche/Variable werden mit einer anderen Sperre
geschützt.
• lock() und unlock() bewirken die Sichtbarkeit der geschützten
Variablen.
• lock() und unlock() müssen paarweise zusammen aufgerufen
werden (try…finally).
• Lock schützt kritische Bereiche dynamisch.
• synchronized schützt kritische Bereiche statisch.
Monitore
•
Von Brinch-Hansen und Hoare entwickelter Mechanismus als sicherer Mechanismus
für nebenläufige Programme entwickelt
•
Ein Monitor ist ein Zusammenschluß von Attributen und Methoden
(Zugriffsoperationen). Methoden werden stets wechselseitig ausgeschlossen
ausgeführt.
•
In Java ist das Monitorkonzept einfach aber auch unsicher implementiert.
•
Schützt kritische Abschnitte
•
Ermöglicht das Warten bis eine Bedingung eintritt
•
Schlüsselwort synchronized
–
–
•
•
•
Bei Methoden wird this als Sperre verwendet
Bei synchronized Blöcken können andere Sperren verwendet werden – jedes Objekte kann
als Sperre funktionieren
Voneinander abhängige Variablen identifzieren und Zugriffe auf diese in kritische
Abschnitte legen
Gemeinsame, veränderliche Variablen sollten immer von genau EINER Sperre
geschützt werden!
Sperren blocken NICHT den Zugriff auf ein Objekt sondern blockieren solange bis
eine Sperre freigegeben ist!
Das this-Objekt verwaltet die Objektsperre.
Synchronized erklärt den Körper einer Methode zum kritischen Bereich.
Synchronized funktioniert reentrant und garantiert aktuelle Werte der Variablen.
Synchronized sorgt automatisch für die Freigabe der Sperre.
Synchronized ist nicht Teil der Signatur!
Das angegebene Objekt verwaltet die Objektsperre. Es ist ein beliebiges Objekt.
Synchronized erklärt den Körper einer Blocks zum kritischen Bereich.
Die beiden Varianten können kombiniert werden, sofern sie sich auf dasselbe Objekt
beziehen.
Diese Variante bietet die Möglichkeit, nur Teile einer Methode zu sichern.
Beispiel für Ablauf mit synchronized
Probleme der Objektsperre
Idee des Sicherheitsfanatikers: „grundsätzlich sollten alle Methoden
gesichert sein, damit keine Interferenz auftritt“!?
Diese Idee ist schlecht:
• Sperren erfordert Aufwand und senkt dadurch die Effizienz.
• Sperren erhöht bei paralleler Hardware die Zeiten bloßen Wartens,
also senkt es nochmals die Effizienz.
• Sperren bedroht die Lebendigkeit eines Programms.
– Lebendigkeit: „ein Programm ist lebendig, wenn alle geplanten
Aktivitäten irgendwann ausgeführtwerden.“
– Deadlock: Mehrere Threads warten auf die Freigabe von Ressourcen
und blockieren sich dabei gegenseitig. Hier geht es um die Freigabe
von Sperren.
– Bei einem Deadlock sind mindestens zwei Threads und mindestens
zwei Objektsperren beteiligt.
Deadlock-Beispiel
Möglicher Ablauf:
1. a startet run() und erhält die Sperre von a.
2. b startet run() und erhält die Sperre von b.
3. a versucht tueWas() zu rufen und wartet auf die Sperre von b.
4. b versucht tueWas() zu rufen und wartet auf die Sperre von a.
5. Die beiden Threads warten gegenseitig aufeinander (deadlock)
Deadlock vermeiden (Beispiel)
Hier kann kein Deadlock entstehen, da das Objekt seine Sperre freigibt, ehe es ein
anderes Objekt aufruft.
Also kann a auf b warten. Aber nicht gleichzeitig b auf a.
Nach Block1 muss das Objekt in einem erlaubten Zustand sein (Klasseninvariante).
Einfache Faustregeln zum
Vermeiden von Deadlocks:
Die folgenden Regeln gelten für Klassen, deren Objekte gleichzeitig von
mehreren
Threads angesprochen werden können:
•
Sperre immer fremden Zugriff, wenn Instanzvariablen eines Objekts
verändert werden.
Andernfalls können unstimmige Veränderungen vorgenommen werden.
•
Sperre immer fremden Zugriff beim Lesen von Instanzvariablen, die
von anderen Threads verändert werden könnten.
Anderfalls können unstimmige Werte zurückgegeben werden (Alternative:
volatile).
•
Sperre fremden Zugriff nach Möglichkeit nicht, wenn Methoden auf
anderen Objekten aufgerufen werden.
Andernfalls können Verklemmungen (deadlock) entstehen.
Die Objektsperre ist ein mächtiger und gefährlicher Mechanismus.
Gemeinsame Objekte sollten sich möglichst einfach verhalten!
Maßnahme 6: Dokumentation
• Kennzeichnen, welche Klassen
threadsicerh sind!
• Policies dokumentieren
• Worüber wird geblockt?
• Annotationen verwenden:
@NotThreadSafe
@ThreadSafe
@Immutable (unveränderlich = automatisch threadsicher)
@GuardedBy("…") (wird durch ein angegebenes Monitor-Objekt geschützt)
NotThreadSafe  ThreadSafe
ThreadSafe  NotThreadSafe
Merke:
Die Verwendung mehrerer threadsicherer Komponenten macht eine
Klasse nicht automatisch threadsicher:
– Es können Abhängigkeiten zwischen den threadsicheren Variablen
bestehen.
– Wenn eine Methode mehrere threadsichere Methoden aufruft, dann
müssen diese unter Umständen synchronisiert werden (z.B. Atomizität)
Nicht threadsichere Komponenten können oft leicht threadsicher
gemacht werden:
- Unveränderbarkeit des Objekts
- Sicherstellen, dass nur eine Thread auf das Objekt zugreift
- Wrapping in threadsichere Objekte
Ausblick
•
•
•
•
Synchronisation von Thread-Abläufen
Einsatz von Klassenbibliotheken
Thread-Pools und Performance
Frameworks
Herunterladen