Ich versuche zu verstehen, warum die Sperre in der Parallelität so wichtig ist, wenn man synchronized (this)
verwenden kann. Im Dummy-Code unten kann ich entweder:
synchronized(this){...}
)Code:
private final ReentrantLock lock = new ReentrantLock();
private static List<Integer> ints;
public Integer getResult(String name) {
.
.
.
lock.lock();
try {
if (ints.size()==3) {
ints=null;
return -9;
}
for (int x=0; x<ints.size(); x++) {
System.out.println("["+name+"] "+x+"/"+ints.size()+". values >>>>"+ints.get(x));
}
} finally {
lock.unlock();
}
return random;
}
Ein ReentrantLock ist unstrukturiert, im Gegensatz zu synchronized
Konstrukten - dh Sie müssen keine Blockstruktur zum Sperren verwenden und können sogar ein Schloss halten über Methoden. Ein Beispiel:
private ReentrantLock lock;
public void foo() {
...
lock.lock();
...
}
public void bar() {
...
lock.unlock();
...
}
Ein solcher Fluss kann nicht über einen einzelnen Monitor in einem Konstrukt synchronized
dargestellt werden.
Abgesehen davon unterstützt ReentrantLock
Sperrenabfrage und nterbrechbare Sperren warten auf das Zeitlimit für die Unterstützung . ReentrantLock
unterstützt auch konfigurierbare Fairness Richtlinie , wodurch eine flexiblere Thread-Planung ermöglicht wird.
Der Konstruktor für diese Klasse akzeptiert einen optionalen fairness Parameter. Wenn
true
gesetzt ist, bevorzugen Sperren das Gewähren des Zugriffs auf den am längsten wartenden Thread. Andernfalls garantiert dieses Schloss keine bestimmte Zugriffsreihenfolge. Programme, die faire Sperren verwenden, auf die von vielen Threads zugegriffen wird, zeigen möglicherweise einen geringeren Gesamtdurchsatz (d. H. Sie sind langsamer; häufig viel langsamer) als diejenigen, die die Standardeinstellung verwenden, weisen jedoch kleinere zeitliche Abweichungen auf, um Sperren zu erhalten und einen Mangel an Hunger zu garantieren. Beachten Sie jedoch, dass die Fairness der Sperren nicht die Fairness der Thread-Planung garantiert. Daher kann einer von vielen Threads, die eine faire Sperre verwenden, diese mehrmals hintereinander erhalten, während andere aktive Threads nicht weiterarbeiten und die Sperre derzeit nicht halten. Beachten Sie auch, dass die MethodetryLock
ohne Zeitangabe die Fairness-Einstellung nicht berücksichtigt. Es ist erfolgreich, wenn die Sperre verfügbar ist, auch wenn andere Threads warten.
ReentrantLock
kann auch skalierbarer sein , viel besser unter höheren Konkurrenzsituationen. Sie können mehr darüber lesen hier .
Diese Behauptung wurde jedoch bestritten; siehe folgenden Kommentar:
Beim Test der Wiedereintrittssperre wird jedes Mal eine neue Sperre erstellt, sodass keine exklusive Sperre vorliegt und die resultierenden Daten ungültig sind. Außerdem bietet der IBM-Link keinen Quellcode für den zugrunde liegenden Benchmark, sodass nicht charakterisiert werden kann, ob der Test überhaupt korrekt durchgeführt wurde.
Wann sollten Sie ReentrantLock
s verwenden? Laut diesem developerWorks-Artikel ...
Die Antwort ist ziemlich einfach - verwenden Sie sie, wenn Sie tatsächlich etwas benötigen, das
synchronized
nicht bereitstellt, wie zeitgesteuertes Warten auf Sperren, unterbrechbares Warten auf Sperren, nicht blockstrukturierte Sperren, mehrere Bedingungsvariablen oder Abfragen von Sperren .ReentrantLock
hat auch Skalierbarkeitsvorteile, und Sie sollten es verwenden, wenn Sie tatsächlich eine Situation haben, die einen hohen Konkurrenzdruck aufweist, aber denken Sie daran, dass die überwiegende Mehrheit dersynchronized
Blöcke kaum jemals eine Konkurrenz aufweist, geschweige denn einen hohen Konkurrenzdruck Streit. Ich würde empfehlen, mit Synchronisation zu entwickeln, bis sich die Synchronisation als unzureichend erwiesen hat, anstatt einfach anzunehmen, dass "die Leistung besser ist", wenn SieReentrantLock
verwenden. Denken Sie daran, dies sind erweiterte Tools für fortgeschrittene Benutzer. (Und wirklich fortgeschrittene Benutzer bevorzugen in der Regel die einfachsten Tools, die sie finden können, bis sie überzeugt sind, dass die einfachen Tools nicht ausreichen.) Machen Sie es wie immer zuerst richtig und sorgen Sie sich dann, ob Sie es schneller machen müssen oder nicht.
ReentrantReadWriteLock
ist eine spezialisierte Sperre, während synchronized(this)
eine universelle Sperre ist. Sie sind ähnlich, aber nicht ganz gleich.
Sie haben recht damit, dass Sie synchronized(this)
anstelle von ReentrantReadWriteLock
verwenden könnten, aber das Gegenteil ist nicht immer der Fall.
Wenn Sie besser verstehen möchten, was ReentrantReadWriteLock
so besonders macht, lesen Sie einige Informationen zur Synchronisierung von Producer-Consumer-Threads.
Im Allgemeinen können Sie sich daran erinnern, dass die Ganzverfahrenssynchronisation und die Mehrzwecksynchronisation (unter Verwendung des Schlüsselworts synchronized
) in den meisten Anwendungen verwendet werden können, ohne zu viel nachzudenken über die Semantik der Synchronisation, aber wenn Sie die Leistung aus Ihrem Code herausholen müssen, müssen Sie möglicherweise andere feinkörnigere oder speziellere Synchronisationsmechanismen untersuchen.
Übrigens kann die Verwendung von synchronized(this)
- und im Allgemeinen das Sperren mit einer öffentlichen Klasseninstanz - problematisch sein, da dadurch Ihr Code potenziellen Dead-Locks ausgesetzt wird, da jemand anderes möglicherweise nicht wissentlich versucht, eine Sperre für Ihr Objekt vorzunehmen sonst im Programm.
Von der Oracle-Dokumentationsseite über ReentrantLock :
Eine wiedereintrittsfähige gegenseitige Ausschlusssperre mit dem gleichen grundlegenden Verhalten und der gleichen Semantik wie die implizite Überwachungssperre, auf die mit synchronisierten Methoden und Anweisungen zugegriffen wird, jedoch mit erweiterten Funktionen.
Ein ReentrantLock gehört dem Thread, der das letzte Mal erfolgreich gesperrt, aber noch nicht entsperrt hat. Ein Thread, der die Sperre aufruft, wird zurückgegeben und die Sperre erfolgreich abgerufen, wenn die Sperre keinem anderen Thread gehört. Die Methode wird sofort zurückgegeben, wenn der aktuelle Thread bereits die Sperre besitzt.
Der Konstruktor für diese Klasse akzeptiert einen optionalen Fairness-Parameter . Wenn "true" festgelegt ist, bevorzugen Sperren das Gewähren des Zugriffs auf den am längsten wartenden Thread . Andernfalls garantiert dieses Schloss keine bestimmte Zugriffsreihenfolge.
ReentrantLock Hauptmerkmale gemäß diesem Artikel
Sie können ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock verwenden, um die Kontrolle über das granulare Sperren von Lese- und Schreibvorgängen zu erlangen.
Schauen Sie sich diesen Artikel von Benjamen zur Verwendung verschiedener Arten von ReentrantLocks an
Sie können wiedereintretende Sperren mit einer Fairness-Richtlinie oder einem Timeout verwenden, um Thread-Hunger zu vermeiden. Sie können eine Thread-Fairness-Richtlinie anwenden. So vermeiden Sie, dass ein Thread für immer auf Ihre Ressourcen wartet.
private final ReentrantLock lock = new ReentrantLock(true);
//the param true turns on the fairness policy.
Die "Fairness Policy" wählt den nächsten ausführbaren Thread aus, der ausgeführt werden soll. Es basiert auf der Priorität, der Zeit seit dem letzten Durchlauf, bla bla
außerdem kann Synchronize unbegrenzt blockieren, wenn es den Block nicht verlassen kann. Für die Wiedereintrittssperre kann ein Timeout festgelegt werden.
Nehmen wir an, dieser Code läuft in einem Thread:
private static ReentrantLock lock = new ReentrantLock();
void accessResource() {
lock.lock();
if( checkSomeCondition() ) {
accessResource();
}
lock.unlock();
}
Da der Thread die Sperre besitzt, können mehrere Aufrufe gesperrt werden (), sodass die Sperre erneut aufgerufen wird. Dies kann mit einem Referenzzähler erreicht werden, sodass keine erneute Sperre erforderlich ist.
Eines ist zu beachten:
Der Name ' ReentrantLock ' gibt eine falsche Meldung über andere Schließmechanismen aus, dass sie nicht wieder eintreten. Dies ist nicht wahr. Die über "synchronisiert" erworbene Sperre ist auch in Java wieder verfügbar.
Der Hauptunterschied besteht darin, dass "synchronisiert" eine interne Sperre verwendet (eine, die jedes Objekt hat), während dies bei der Sperr-API nicht der Fall ist.
Synchronisierte Sperren bietet keinen Warteschlangenmechanismus, bei dem nach der Ausführung eines Threads jeder parallel laufende Thread die Sperre erhalten kann. Aufgrund dessen hat der Thread, der sich im System befindet und über einen längeren Zeitraum läuft, nie die Möglichkeit, auf die gemeinsam genutzte Ressource zuzugreifen, was zu einem Hunger führt.
Wiedereintrittssperren sind sehr flexibel und haben eine Fairness-Richtlinie, bei der wir sicherstellen können, dass der länger wartende Thread die Chance bekommt, wenn ein Thread längere Zeit wartet und nachdem der aktuell ausgeführte Thread abgeschlossen ist Hierdurch wird der Durchsatz des Systems verringert und der Zeitaufwand erhöht.