Parallele Programmierung (3IB)
=> Grundlage: Hettel und Tran (2016, Kap. 8)
Lock-InterfaceReentrantLock-Implementierung von LockReadWriteLockStampedLocksynchronized blockierter Thread kann nicht mit interrupt() unterbrochen werden. interrupt() wirkt sich erst nach der Blockierung aus.ReentrantLockLock-Implementierungen und ihre BeziehungenLock-InterfacelockInterruptably() blockierter Thread kann durch interrupt() unterbrochen werden (\(\to\) InterruptedException)tryLock() liefert false, falls `Lock `-Instanz schon belegt istsynchronizedsynchronized blockierter Thread kann nicht mit interrupt() unterbrochen werden. interrupt() wirkt sich erst nach der Blockierung aus.Lock-ImplementierungenLock-Interface und -Implementierungentry … finally wird sichergestellt, dass der Lock auf jeden Fall am Ende gelöst wird.lock() und unlock() können von unterschiedlichen Methoden aus aufgerufen werden.Lock-Interface und -Implementierungenfairness-Parameter gibt an, ob als nächstes der am längsten wartende Thread entblockiert wird oder nicht.Lock-Interface und -ImplementierungentryLock() prüfen, ob Lock belegt und schließen oder entwas anderes tunpp.06.01-SynchInterruptReadWriteLockLock-Implementierungen und ihre BeziehungenReadWriteLockReadLock und WriteLock besitzt
ReadLock: mehrere dürfen in den kritischen Abschnitt, wenn der dazugehörende WriteLock nicht geschlossen ist (“nicht-exklusiver” Lock).WriteLock: nur einer darf in den kritischen Abschnitt und auch nur, wenn der ReadLock nicht gerade benutzt wird (“exklusiver” Lock).ReadWriteLockrLock und wLock werden benutzt wie die bisherigen Lock-ImplementierungenReadWriteLock wird je ein Read-Lock und ein Write-Lock erzeugt.Lock-Interface.ReadriteLock-Objekt bekommen. Das ist im Sequenzdiagramm nur durch new(lock) angedeutet. Natürlich erwartet der Konstruktor von Thread kein Lock-Objekt.ReadWriteLock sind miteinander “verschränkt” und wirken auf den sie erzeugenden ReadWriteLock zurück. Im Sequenzdiagramm ist das nur durch “namenlose Nachrichten” angedeutet.rl und wl auf lock nicht dargestellt.t1 schließt zuerst den Read-Locklock: gelbt2 darf am Read-Lock teilnehmen und wird nicht blockiert.t3 möchte den Write-Lock und wird blockiert.t3 wird erst entblockiert, wenn sowohl t1 als auch t2 den Read-Lock wieder freigegeben haben. Der Write-Lock ist nun von t3 gesperrt \(\to\) lock: blaut1 möchte den Read-Lock und wird blockiert. Erst wenn t3 den Write-Lock freigibt, bekommt t1 den Read-Locklock: gelbt1 gibt am Ende den Read-Lock wieder frei.rl und wl auf lock nicht dargestellt.t1 schließt zuerst den Read-Locklock: gelbt3 möchte den Write-Lock und wird blockiert.t2 darf nun wg. offener Lock-Anforderung von t3 nicht mehr am Read-Lock teilnehmen und wird blockiert.t3 wird wg. der Reihenfolge der Anforderung als nächstes entblockiert, wenn t1 freigibt. Der Write-Lock ist nun von t3 gesperrt \(\to\) lock: blaut2 wird erst entblockiert, wenn t3 den Lock freigibt. \(\to\) lock: gelbt2 gibt am Ende den Read-Lock wieder frei.lock() ist bestimmendStampedLockLock-Implementierungen und ihre BeziehungenLock relativ hoch. synchronized kann von der Laufzeit her schneller sein.StampedLock nützlich bei großem LeseanteilStampedLock ModiEin StampedLock besteht aus Stamp und Modus
writeLock() blockiert für exklusiven Schreibzugriff; liefert eine long ID (“stamp”), die für unlockWrite(long) benutzt werden kann.readLocks und tryOptimisticRead möglich.readLock() wartet auf nicht exklusiven-Zugriff; liefert eine long ID (“stamp”), die für unlockRead(long) benutzt werden kann.tryOptimisticRead() liefert nur dann eine long ID (“stamp”), falls der Lock gerade nicht im Modus “Writing” ist. validate(long) liefert true, falls der Lock nicht zum Schreiben gesperrt wurde, seit die ID vergeben wurde.StampedLockt1 bekommt zuerst den “nicht-exklusiven” Lock (“Read-Lock”) \(\to\) lock ist gelbt3 möchte den “exklusiven” Lock (“Write-Lock”) und wird blockiert, bis der nicht-exklusive Lock endet \(\to\) t1 ist rott2 kommt zu t1 in den “nicht-exklusiven” Lockt1 verlässt den “nicht-exklusiven” Lockt3 bleibt vorerst weiterhin blockiert, solange bis der letzte Thread den “nicht-exklusiven” Lock verlässtt2 tut dies, daraufhin bekommtt3 den “exklusiven” Lock \(\to\) lock ist blaut1 möchte wieder einen “nicht-exklusiven” Lock, wird aber wg. t3 blockiert \(\to\) t1 ist rott3 gibt den “exklusiven” Lock zurück, erst dann wirdt1 entblockiert und erhält den “nicht-exklusiven” Lock \(\to\) lock ist gelbStampedLockStampedLock-Objekt “markieren” (anmelden)StampedLock-Objekt nachfragen, ob es seit der Anmeldung Write-Locks gegeben hat (Read-Locks sind unkritisch)StampedLockStampedLockvar lock = new StampedLock();
var stamp = lock.tryOptimisticRead();
//... kritischer Abschnitt ("nicht exklusiv")
if (!lock.validate(stamp)) {
// nicht erfolgreich => das bisherige ist möglicherweise
// inkonsistent und muss zurückgerollt werden; eine mögliche
// Strategie: nochmal pessimistisch "non-excl." gelockt probieren:
stamp = lock.readLock();
try {
//... kritischer Abschnitt (ReadLock)
} finally {
lock.unlockRead(stamp);
}
}andere Muster denkbar (z.B. wie bei Atomics: weiter optimistisch versuchen, bis erfolgreich \(\to\) s. nächstes Beispiel)
t1 versucht optimistisch lock zu nutzent3 schließt lock exklusiv (t1 bekommt davon erstmal nichts mit und t3 wird auch nicht blockiert) \(\to\) lock blaut1 ist fertig, stellt aber Misserfolg fest und versucht es erneut opt. (t1 wird nicht blockiert, obwohl t3 exkl. Lock hat)t3 gibt lock wieder frei \(\to\) lock nicht mehr blaut2 fordert erfolgreich nicht-exkl. Lock an. lock ist frei, t2 blockiert nicht. lock nun nicht-exkl. vergeben \(\to\) lock gelbt1 ist fertig, stellt aber Misserfolg fest und versucht es erneut opt. (t1 wird nicht blockiert)t2 ist fertig \(\to\) lock ist nicht mehr gelbt1 ist fertig und stellt nun fest, dass das opt. Lesen endlich erfolgreich warReentrantLock, ReentrantReadWriteLock, StampedLock und synchronized”pp.06.02-LockTimingpermitCount): Anzahl an noch erlaubten Nutzern.release() und acquire() (blockiert, falls permitCount == 0)Hettel und Tran (2016, 120)