387 Stimmen

Warum ein ReentrantLock verwenden, wenn man synchronized(this) verwenden kann?

Ich versuche zu verstehen, warum die Sperre bei Gleichzeitigkeit so wichtig ist, wenn man die synchronized (this) . In dem Dummy-Code unten kann ich beides tun:

  1. die gesamte Methode synchronisieren oder den gefährdeten Bereich synchronisieren ( synchronized(this){...} )
  2. ODER sperren Sie den anfälligen Codebereich mit einem ReentrantLock.

Código:

    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;
}

559voto

obataku Punkte 28674

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).

16voto

Mike Dinescu Punkte 51297

ReentrantReadWriteLock ist eine spezialisierte Sperre, während synchronized(this) ist ein Allzweckschloss. Sie sind ähnlich, aber nicht ganz dasselbe.

Sie haben recht, dass Sie die synchronized(this) anstelle von ReentrantReadWriteLock aber das Gegenteil ist nicht immer der Fall.

Wenn Sie besser verstehen möchten, was die ReentrantReadWriteLock spezielle Informationen über die Synchronisierung von Producer-Consumer-Threads nachschlagen.

Im Allgemeinen können Sie sich merken, dass die Synchronisierung ganzer Methoden und die allgemeine Synchronisierung (unter Verwendung der synchronized Schlüsselwort) kann in den meisten Anwendungen verwendet werden, ohne dass man zu viel ü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 spezielle Synchronisationsmechanismen untersuchen.

Übrigens, die Verwendung von synchronized(this) - und generell das Sperren mit einer öffentlichen Klasseninstanz - kann problematisch sein, weil es Ihren Code für potenzielle Deadlocks öffnet, weil jemand anderes, ohne es zu wissen, versuchen könnte, Ihr Objekt an einer anderen Stelle im Programm zu sperren.

14voto

Ravindra babu Punkte 45577

Aus der Oracle-Dokumentationsseite über ReentrantLock :

Eine reentrante gegenseitige Ausschlusssperre mit demselben grundlegenden Verhalten und derselben Semantik wie die implizite Monitorsperre, auf die mit synchronisierten Methoden und Anweisungen zugegriffen wird, jedoch mit erweiterten Fähigkeiten.

  1. A ReentrantLock ist im Besitz des Threads, der ihn zuletzt erfolgreich gesperrt, aber noch nicht entsperrt hat. Ein Thread, der lock aufruft, kehrt zurück, wenn er die Sperre erfolgreich erworben hat und die Sperre nicht im Besitz eines anderen Threads ist. Die Methode kehrt sofort zurück, wenn der aktuelle Thread die Sperre bereits besitzt.

  2. Der Konstruktor für diese Klasse akzeptiert eine optionale Fairness Parameter. Wenn er auf true gesetzt ist, wird unter contention, Sperren bevorzugen den Zugriff auf den am längsten wartenden Thread . Ansonsten garantiert diese Sperre keine bestimmte Zugriffsreihenfolge.

ReentrantLock Hauptmerkmale in diesem Sinne artículo

  1. Fähigkeit, unterbrechbar zu sperren.
  2. Möglichkeit der Zeitüberschreitung beim Warten auf die Sperre.
  3. Befugnis, ein faires Schloss zu schaffen.
  4. API, um eine Liste der wartenden Threads für eine Sperre zu erhalten.
  5. Flexibilität bei der Suche nach einer Sperre ohne Blockierung.

Sie können verwenden ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock um weitere Kontrolle über granulare Sperren bei Lese- und Schreibvorgängen zu erlangen.

Schauen Sie sich dies an artículo von Benjamen über die Verwendung von verschiedenen Arten von ReentrantLocks

6voto

Sumit Kapoor Punkte 871

Synchronisierte Sperren bietet keinen Mechanismus einer Warteschlange, in der nach der Ausführung eines Threads jeder parallel laufende Thread die Sperre erwerben kann. Dies führt dazu, dass der Thread, der im System vorhanden ist und über einen längeren Zeitraum läuft, nie die Möglichkeit hat, auf die gemeinsame Ressource zuzugreifen, was zu einem Aushungern führt.

Wiederkehrende Schlösser sind sehr flexibel und haben eine Fairness-Politik, in der, wenn ein Thread länger wartet und nach der Fertigstellung des aktuell ausgeführten Threads können wir sicherstellen, dass der länger wartende Thread die Chance erhält, auf die gemeinsame Ressource zuzugreifen, wodurch der Durchsatz des Systems sinkt und es zeitaufwändiger wird.

5voto

j2emanue Punkte 56131

Sie können ablaufinvariante Sperren mit einer Fairness-Richtlinie oder einem Timeout verwenden, um ein Aushungern von Threads zu vermeiden. Sie können eine Fairness-Richtlinie für Threads anwenden, um zu vermeiden, dass ein Thread ewig wartet, um an Ihre Ressourcen zu gelangen.

private final ReentrantLock lock = new ReentrantLock(true);
//the param true turns on the fairness policy. 

Die "Fairness-Politik" wählt den nächsten lauffähigen Thread zur Ausführung aus. Sie basiert auf Priorität, Zeit seit dem letzten Lauf, blah blah

auch, Synchronize kann auf unbestimmte Zeit blockieren, wenn es der Blockierung nicht entkommen kann. Bei Reentrantlock kann ein Timeout gesetzt werden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X