Parallele Programmierung (3IB)
=> Grundlage: Hettel und Tran (2016, Kap. 8)
Lock
-InterfaceReentrantLock
-Implementierung von Lock
ReadWriteLock
StampedLock
synchronized
blockierter Thread kann nicht mit interrupt()
unterbrochen werden. interrupt()
wirkt sich erst nach der Blockierung aus.ReentrantLock
Lock
-Implementierungen und ihre BeziehungenLock
-Interfacepublic interface Lock {
public void lock();
public void lockInterruptibly()
throws InterruptedException;
public boolean tryLock();
public boolean tryLock(long time, TimeUnit unit)
throws InterruptedException;
public void unlock();
public Condition newCondition();
}
lockInterruptably()
blockierter Thread kann durch interrupt()
unterbrochen werden (\(\to\) InterruptedException
)tryLock()
liefert false
, falls `Lock
`-Instanz schon belegt istsynchronized
synchronized
blockierter Thread kann nicht mit interrupt()
unterbrochen werden. interrupt()
wirkt sich erst nach der Blockierung aus.Lock
-ImplementierungenLock
-Interface und -Implementierungen
Lock lock = new ReentrantLock();
lock.lock();
try {
//... kritischer Abschnitt
} finally {
lock.unlock();
}
try
… 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 -Implementierungenboolean fairness;
//...
Lock lock = new ReentrantLock(fairness);
lock.lock();
try {
//... kritischer Abschnitt
} finally {
lock.unlock();
}
fairness
-Parameter gibt an, ob als nächstes der am längsten wartende Thread entblockiert wird oder nicht.Lock
-Interface und -ImplementierungenLock lock = new ReentrantLock();
if(lock.tryLock()) {
try {
//... kritischer Abschnitt
} finally {
lock.unlock();
}
} else {
//... etwas anderes tun...
}
tryLock()
prüfen, ob Lock
belegt und schließen oder entwas anderes tunpp.06.01-SynchInterrupt
ReadWriteLock
Lock
-Implementierungen und ihre BeziehungenReadWriteLock
ReadLock
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).ReadWriteLock
public interface ReadWriteLock {
public Lock readLock();
public Lock writeLock();
}
ReadWriteLock lock = new ReentrantReadWriteLock(fairness);
Lock rLock = lock.readLock();
Lock wLock = lock.writeLock();
rLock
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 bestimmendStampedLock
Lock
-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.StampedLock
t1
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 gelbStampedLock
StampedLock
-Objekt “markieren” (anmelden)StampedLock
-Objekt nachfragen, ob es seit der Anmeldung Write-Locks gegeben hat (Read-Locks sind unkritisch)StampedLock
StampedLock
var 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-LockTiming
permitCount
): Anzahl an noch erlaubten Nutzern.release()
und acquire()
(blockiert, falls permitCount == 0
)Hettel und Tran (2016, 120)