Thread Synchronisation
Note
Thread.currentThread().join()
Programm wartet ewig, weil es auf die Beendigung von sich selbst wartet
- Synchronisation = Einschränkung der Nebenläufigkeit
- Fälle für Synchronisation
- Gegenseitiger Ausschluss: Eine Ressource kann nur von jemandem auf einmal benutzt werden
- Warten auf Bedingung
Gemeinsame Ressourcen
- Threads können sich gegenseitig stören, weil sie sich Adressraum und Heap teilen
- In Java können lokale Variablen (closure) von Threads nur read-only zugegriffen werden
Kritische Abschnitte
- Race Condition: Fehler aufgrund von unkontrollierter Nebenläufigkeit
- Race Condition Szenario
- Beide Threads lesen die aktuelle Balance nacheinander in ein Register
- Der zweite Thread, der die Balance zurück schreibt, überschreibt den anderen Wert
- -> Lost Update
- Ein Programmstück, dass eine Race Condition verursachen könnte, ist eine "Critical Section"
- Naiver Ansatz: "Locked"-Boolean einführen und evtl. warten, bis
locked == false
ist- Problem: Prüfung des boolean und Zuweisung nicht atomar -> Gleiches Problem wie vorher
Gegenseitiger Ausschluss
- Eigene Implementierung nicht trivial (gibt spezielle Algorithmen)
- Weitere Probleme mit CPU-Optimierungen, Reihenfolge der Instruktionen ist nicht garantiert
- Busy-Waiting (Endlosschleife) ist ineffizient
Java Monitor Konzept
public synchronized void deposit(int amount) { this.balance += amount; // Kritischer Abschnitt }
- Body der Funktion wird unter gegenseitigem Ausschluss ausgeführt
- Nur ein Thread auf einmal kann die Methode ausführen
- Bei jedem Eintritt wird das Objekt gelockt (Monitor-Lock)
-
Gilt für das ganze Objekt, auch andere synchronized-Methoden schliessen sich gegenseitig aus
-
Freigabe des Locks bei Ende, return oder unhandled Exception
-
Recursive Locks:
synchronized
Method ruft anderesynchronized
Method auf (nested Lock)- Ist in Java kein Problem, in anderen Sprachen u.U. schon
Synchronized Statements
synchronized(object) { statements }
object
bezieht sich auf das Objekt, das gelockt wird- Bei
synchronized
mit Static-Klassen wird<class>.class
mitgegeben, dann wird die Klasse gelockt (Singleton)
Warten auf Bedingung
- Naiver Ansatz: in Schleife warten, bis Bedingung erfüllt
- Funktioniert nicht: Lock wird nicht aufgelöst, es kann niemand anders das Objekt bearbeiten
- Ineffizent, immer zu pollen
Wait & Signal
- Monitor: Gegenseitiger Ausschluss + Wait & Signal Mechanismus
- Nur ein Thread kann "im Monitor" sein
- Spezieller Wartebereich für Threads, die auf eine Bedingung warten
- Sie werden signalisiert, sobald die Bedingung erfüllt ist
class BankAccount { private int balance = 0; public synchronized void withdraw(int amount) throws InterruptedException { while (amount > balance) { wait(); } balance -= amount; } public synchronized void deposit(int amount) { balance += amount; notifyAll(); } }
wait()
setzt den Thread in die Warteschlange zum Warten auf eine BedingungnotifyAll()
weckt alle im Monitor wartenden Thread auf- Behält aber den Monitor-Lock für weitere Instruktionen ("Single and Continue")
- Ein aufgeweckter Thread geht wieder in den "äusseren" Warteraum, genau wie alle anderen eintrittswilligen Threads
- Wieso funktioniert das?
wait()
setzt sich in den "Warteraum" und gibt den Monitor (den Lock auf das Objekt) frei
notify()
weckt einen beliebigen Thread im "Warteraum" auf- Die Reihenfolge im "Warteraum" ist in Java nicht spezifiziert (in .NET FIFO)
- Spurious Wakeup: Ein wartender Thread kann "fälschlicherweise" aufgeweckt werden, ohne dass
notify
aufgerufen wurde- Problem von POSIX-Threads
Fallstricke beim Java Monitor
Wait mit If
- Nach dem
wait()
muss die Bedingung wieder überprüft werden- In der Zwischenzeit könnte nach dem
notify()
und vor dem Eintreten die Bedingung wieder invalidiert werden - Daher:
while (!conditioin) { wait(); }
- In der Zwischenzeit könnte nach dem
Single-Notify
notify()
weckt ein beliebiger einzelner Thread auf- Wenn immer ein falscher Thread aufgeweckt wird, terminiert das Programm nie
- Lösung:
notifyAll()
verwenden
Effizenz-Probleme
notifyAll()
weckt alle auf, bei vielen Threads ist das viel Overhead für die Überprüfung der Bedingung- Keine Fairness: Keine Warteschlange spezifiziert
- Signal-and-Continue: Informierte muss neu anstehen und können ständig überholt werden
- Auch bei FIFO (.NET) nicht garantiert fair