webentwicklung-frage-antwort-db.com.de

Warum ein ReentrantLock verwenden, wenn man synchronisiert (dies) verwenden kann?

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:

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

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

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 ReentrantLockSperrenabfrage 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 Methode tryLock 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 ReentrantLocks 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 der synchronized 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 Sie ReentrantLock 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.

446
oldrinb

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.

13
Mike Dinescu

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.

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

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

  1. Fähigkeit, unterbrechbar zu sperren.
  2. Möglichkeit einer Zeitüberschreitung beim Warten auf die Sperre.
  3. Macht, faire Sperre zu schaffen.
  4. API zum Abrufen der Liste der wartenden Threads für die Sperre.
  5. Flexibilität, um nach einem Schloss zu suchen, ohne es zu blockieren.

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

8
Ravindra babu

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.

0
j2emanue

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.

0
RonTLV

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.

0
Summer

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.

0
Sumit Kapoor