A ReentrantLock es unstrukturiert im Gegensatz zu synchronized
Konstrukte - d.h. Sie müssen keine Blockstruktur zum Sperren verwenden und können eine Sperre sogar methodenübergreifend halten. Ein Beispiel:
private ReentrantLock lock;
public void foo() {
...
lock.lock();
...
}
public void bar() {
...
lock.unlock();
...
}
Ein solcher Fluss lässt sich unmöglich durch einen einzigen Monitor in einem synchronized
konstruieren.
Davon abgesehen, ReentrantLock
unterstützt [Sperrabfrage](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantLock.html#tryLock()) y unterbrechbare Wartesperren, die eine Zeitüberschreitung unterstützen . ReentrantLock
hat auch Unterstützung für konfigurierbar Fairness Politik und ermöglicht so eine flexiblere Thread-Planung.
Der Konstruktor für diese Klasse akzeptiert eine optionale Fairness Parameter. Wenn gesetzt true
Wenn die Sperren unter Konkurrenz stehen, wird der Zugriff auf den am längsten wartenden Thread bevorzugt gewährt. Ansonsten garantiert diese Sperre keine bestimmte Zugriffsreihenfolge. Programme, die faire Sperren verwenden, auf die viele Threads zugreifen, können einen geringeren Gesamtdurchsatz aufweisen (d. h. sie sind langsamer, oft sehr viel langsamer) als solche, die die Standardeinstellung verwenden, haben aber geringere Abweichungen bei den Zeiten für den Erhalt von Sperren und garantieren, dass sie nicht verhungern. Beachten Sie jedoch, dass die Fairness der Sperren keine Fairness der Thread-Planung garantiert. So kann einer von vielen Threads, die eine faire Sperre verwenden, diese mehrmals hintereinander erhalten, während andere aktive Threads nicht vorankommen und die Sperre nicht gerade halten. Beachten Sie auch, dass die untimed tryLock
Methode wird die Fairness-Einstellung nicht berücksichtigt. Sie wird erfolgreich sein, wenn die Sperre verfügbar ist, auch wenn andere Threads warten.
ReentrantLock
Mai auch sein skalierbarer Die Leistung ist bei höherer Beanspruchung viel besser. Sie können mehr darüber lesen aquí .
Diese Behauptung ist jedoch umstritten; siehe die folgende Anmerkung:
Beim Test der ablaufenden Sperre wird jedes Mal eine neue Sperre erstellt, so dass es keine exklusive Sperre gibt und die resultierenden Daten ungültig sind. Außerdem bietet der IBM-Link keinen Quellcode für den zugrunde liegenden Benchmark, so dass es unmöglich ist, festzustellen, ob der Test überhaupt korrekt durchgeführt wurde.
Wann sollten Sie die ReentrantLock
s? Laut diesem developerWorks Artikel...
Die Antwort ist ziemlich einfach: Verwenden Sie es, wenn Sie etwas brauchen, das es bietet. synchronized
nicht, wie z. B. zeitgesteuerte Sperren, unterbrechbare Sperren, nicht blockstrukturierte Sperren, mehrere Bedingungsvariablen oder Sperrenabfragen. ReentrantLock
hat auch Vorteile in Bezug auf die Skalierbarkeit, und Sie sollten es verwenden, wenn Sie tatsächlich eine Situation haben, in der es zu einer starken Beanspruchung kommt, aber denken Sie daran, dass die große Mehrheit der synchronized
Blöcke weisen so gut wie nie einen Konflikt auf, geschweige denn einen hohen Konflikt. Ich würde dazu raten, mit Synchronisierung zu entwickeln, bis sich die Synchronisierung als unzureichend erwiesen hat, anstatt einfach davon auszugehen, dass "die Leistung besser sein wird", wenn Sie ReentrantLock
. Denken Sie daran, dass dies fortgeschrittene Werkzeuge für fortgeschrittene Benutzer sind. (Und wirklich fortgeschrittene Benutzer neigen dazu, die einfachsten Werkzeuge zu bevorzugen, die sie finden können, bis sie überzeugt sind, dass die einfachen Werkzeuge unzureichend sind). Wie immer gilt: Machen Sie es zuerst richtig, und machen Sie sich dann Gedanken darüber, ob Sie es schneller machen müssen oder nicht.
Ein letzter Aspekt, der in naher Zukunft an Bedeutung gewinnen wird, hat mit folgenden Aspekten zu tun Java 15 und Projekt Loom . In der (neuen) Welt der virtuellen Threads könnte der zugrunde liegende Planer viel besser mit ReentrantLock
als sie es mit synchronized
Das ist zumindest in der ersten Version von Java 15 der Fall, kann aber später noch optimiert werden.
In der aktuellen Loom-Implementierung kann ein virtueller Thread in zwei Situationen gepinnt werden: wenn sich ein nativer Frame auf dem Stack befindet - wenn Java-Code nativen Code (JNI) aufruft, der wiederum Java aufruft - und wenn er sich innerhalb einer synchronized
Block oder Methode. In diesen Fällen blockiert das Blockieren des virtuellen Threads auch den physischen Thread, der ihn ausführt. Sobald der native Aufruf abgeschlossen oder der Monitor freigegeben ist (die synchronized
Block/Methode verlassen wird) wird der Thread entkoppelt.
Wenn Sie eine gemeinsame E/A-Operation haben, die durch eine synchronized
ersetzen Sie den Monitor durch einen ReentrantLock
damit Ihre Anwendung in vollem Umfang von der Skalierbarkeit von Loom profitiert, noch bevor wir das Pinning durch Monitore beheben (oder, noch besser, die leistungsstärkere StampedLock
wenn Sie können).