Warum sollte man eine ReentrantLock verwenden, wenn man synchronisierte (this) benutzen kann?

Ich versuche zu verstehen, was die Sperre in Nebenläufigkeit so wichtig macht, wenn man synchronized (this) . Im Dummy-Code unten kann ich entweder:

  1. synchronisierte die gesamte Methode oder synchronisierte den anfälligen Bereich (synchronisierte (this) {…})
  2. ODER sperren Sie den anfälligen Codebereich mit einer ReentrantLock.

Code:

  private final ReentrantLock lock = new ReentrantLock(); private static List ints; public Integer getResult(String name) { . . . lock.lock(); try { if (ints.size()==3) { ints=null; return -9; } for (int x=0; x>>>"+ints.get(x)); } } finally { lock.unlock(); } return random; } 

Ein ReentrantLock ist im Gegensatz zu synchronized Konstrukten unstrukturiert – dh Sie müssen zum Sperren keine Blockstruktur verwenden und können sogar Methoden sperren. Ein Beispiel:

 private ReentrantLock lock; public void foo() { ... lock.lock(); ... } public void bar() { ... lock.unlock(); ... } 

Ein solcher Fluss ist unmöglich über einen einzigen Monitor in einem synchronized Konstrukt darzustellen.


Abgesehen ReentrantLock unterstützt ReentrantLock Lock Polling und Interruptable Lock Waits , die ReentrantLock unterstützen. ReentrantLock auch konfigurierbare Fairness- Richtlinien , die eine flexiblere Thread-Planung ermöglichen.

Der Konstruktor für diese class akzeptiert einen optionalen Fairness- Parameter. Wenn diese true aktiviert ist, werden Sperren bevorzugt, um Zugriff auf den am längsten wartenden Thread zu gewähren. Andernfalls garantiert diese Sperre keine bestimmte Zugriffsreihenfolge. Programme, die faire Sperren verwenden, auf die von vielen Threads zugegriffen wird, zeigen möglicherweise einen niedrigeren Gesamtdurchsatz (dh sind langsamer, oft viel langsamer) als diejenigen, die die Standardeinstellung verwenden, haben jedoch kleinere Abweichungen in Zeiten, um Sperren zu erhalten und Mangel an Hunger zu garantieren. Beachten Sie jedoch, dass die Fairness der Sperren keine Gewähr für die Fairness der Thread-Planung bietet. Daher kann einer von vielen Threads, die eine faire Sperre verwenden, diese mehrfach hintereinander erhalten, während andere aktive Threads nicht fortfahren und die Sperre derzeit nicht halten. Beachten Sie auch, dass die tryLock Methode ohne tryLock die Fairnesseinstellung nicht berücksichtigt. Es wird erfolgreich sein, wenn die Sperre verfügbar ist, auch wenn andere Threads warten.


ReentrantLock kann auch besser skalierbar sein und bei höheren Konflikten viel besser ReentrantLock . Sie können hier mehr darüber lesen.

Dieser Anspruch wurde jedoch bestritten; Siehe den folgenden Kommentar:

Im Reentrant-Lock-Test wird jedes Mal eine neue Sperre erstellt, daher gibt es keine exklusive Sperre und die resultierenden Daten sind ungültig. Außerdem bietet der IBM Link keinen Quellcode für den zugrunde liegenden Benchmark, so dass es unmöglich ist zu charakterisieren, 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 die synchronized nicht bereitstellt, z. B. zeitgesteuerte Sperrwartezeiten, unterbrechbare Sperrwartezeiten, nicht blockierte Sperrungen, mehrere Bedingungsvariablen oder Sperrabfrage. ReentrantLock hat auch Skalierbarkeitsvorteile, und Sie sollten es verwenden, wenn Sie tatsächlich eine Situation haben, die eine hohe Konkurrenz aufweist, aber denken Sie daran, dass die große Mehrheit der synchronized Blöcke kaum jemals eine Konkurrenz, geschweige denn eine hohe Konkurrenz aufweist. Ich würde empfehlen, mit Synchronisierung zu entwickeln, bis sich die Synchronisierung als unzureichend erwiesen hat, anstatt einfach anzunehmen, dass “die performance besser wird”, wenn Sie ReentrantLock . Denken Sie daran, dies sind erweiterte Tools für fortgeschrittene Benutzer. (Und wirklich fortgeschrittene Benutzer neigen dazu, die einfachsten Tools zu bevorzugen, die sie finden können, bis sie davon überzeugt sind, dass die einfachen Tools nicht ausreichen.) Wie immer, machen Sie es zuerst richtig und sorgen Sie sich dann darum, ob Sie es schneller machen müssen.

Eine Reentrant-Sperre ermöglicht es dem Halter, Code-Blöcke einzugeben, selbst nachdem er die Sperre bereits erhalten hat, indem er andere Code-Blöcke eingegeben hat. Bei einer nicht-wiedereintretenden Sperre würde der Sperrenhalterblock sich selbst blockieren, da er die Sperre, die er von einem anderen Codeblock erhalten hat, freigeben müsste, um dieselbe Sperre erneut zu erreichen, um in die verschachtelte Sperre zu gelangen, die einen Codeblock erfordert

  public synchronized void functionOne() { // do something functionTwo(); // do something else // redundant, but permitted... synchronized(this) { // do more stuff } } public synchronized void functionTwo() { // do even more stuff! } 

Erweiterte functionen der Wiedereintrittssperre umfassen: –

  1. Die Fähigkeit, mehr als eine Zustandsvariable pro Monitor zu haben. Monitore, die das synchronisierte Keyword verwenden, können nur eins haben. Dies bedeutet, dass Reentrant-Sperren mehr als eine wait () / notify () – Warteschlange unterstützen.
  2. Die Fähigkeit, das Schloss “fair” zu machen. “[fair] Sperren bevorzugen den Zugriff auf den am längsten wartenden Thread. Andernfalls garantiert diese Sperre keine bestimmte Zugriffsreihenfolge.” Synchronisierte Blöcke sind unfair.
  3. Die Fähigkeit zu überprüfen, ob die Sperre gehalten wird.
  4. Die Fähigkeit, die Liste der Threads zu erhalten, die auf die Sperre warten.

Nachteile von Wiedereintrittssperren sind: –

Sie müssen eine Importanweisung hinzufügen. Sie müssen Lock Acquisitions in einen try / finally Block einbinden. Dies macht es hässlicher als das synchronisierte Keyword. Das synchronisierte Schlüsselwort kann in Methodendefinitionen eingefügt werden, die die Notwendigkeit eines Blocks vermeiden, der die Verschachtelung reduziert.

Wann zu verwenden: –

  1. ReentrantLock ist möglicherweise eher zu verwenden, wenn Sie einen Thread implementieren müssen, der eine verknüpfte Liste durchläuft, den nächsten Knoten sperrt und dann den aktuellen Knoten entsperrt.
  2. Synchronisiertes Schlüsselwort eignet sich in Situationen wie Lock-Roaring, adaptivem Drehen, verzerrtem Sperren und dem Potenzial für Lock-Elision über die Escape-Analyse. Diese Optimierungen werden derzeit nicht für ReentrantLock implementiert.

Für weitere Informationen .

ReentrantReadWriteLock ist eine spezielle Sperre, während synchronized(this) eine allgemeine Sperre ist. Sie sind ähnlich, aber nicht ganz gleich.

Sie haben Recht, dass Sie synchronized(this) anstelle von ReentrantReadWriteLock aber das Gegenteil ist nicht immer wahr.

Wenn Sie besser verstehen ReentrantReadWriteLock , was ReentrantReadWriteLock speziell macht, ReentrantReadWriteLock einige Informationen über die Synchronisierung von Producer-Consumer-Threads.

Im Allgemeinen können Sie sich daran erinnern, dass die Synchronisierung ganzer Methoden und die Synchronisierung allgemeiner Zwecke (mithilfe des synchronized Schlüsselworts) in den meisten Anwendungen verwendet werden können, ohne zu viel über die Semantik der Synchronisierung nachzudenken, aber wenn Sie die performance aus Ihrem Code herausholen müssen andere feinkörnigere oder speziellere Synchronisationsmechanismen zu erforschen.

Übrigens kann die Verwendung synchronized(this – und im Allgemeinen das Sperren mit einer öffentlichen classninstanz – problematisch sein, da sie Ihren Code für potentielle Deadlocks öffnet, weil jemand anders wissentlich nicht versucht, sich an Ihrem Objekt irgendwo anders in der database zu sperren Programm.

Von oracle Dokumentationsseite über ReentrantLock :

Ein reentranter wechselseitiger Ausschluss Sperren Sie mit dem gleichen grundlegenden Verhalten und der gleichen Semantik wie die implizite Überwachungssperre, auf die mit synchronisierten Methoden und statementen zugegriffen wird, jedoch mit erweiterten functionen.

  1. Ein ReentrantLock gehört dem Thread, der zuletzt erfolgreich gesperrt , aber noch nicht entsperrt wurde. Eine Threadaufrufsperre wird zurückgegeben, wobei die Sperre erfolgreich erworben wird, wenn die Sperre nicht einem anderen Thread gehört. Die Methode kehrt sofort zurück, wenn der aktuelle Thread bereits die Sperre besitzt.

  2. Der Konstruktor für diese class akzeptiert einen optionalen Fairness- Parameter. Wenn diese Option aktiviert ist, werden Sperren bevorzugt, um Zugriff auf den am längsten wartenden Thread zu gewähren . Andernfalls garantiert diese Sperre keine bestimmte Zugriffsreihenfolge.

ReentrantLock Hauptmerkmale gemäß diesem Artikel

  1. Fähigkeit, unterbrechbar zu sperren.
  2. Zeitüberschreitung beim Warten auf Sperre
  3. Macht, ein faires Schloss zu schaffen.
  4. API, um eine Liste mit wartendem Thread für die Sperre zu erhalten.
  5. Flexibilität zum Sperren ohne Blockierung.

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

Werfen Sie einen Blick auf diesen Artikel von Benjamen über die Verwendung verschiedener Arten von ReentrantLocks

Sie können Reentrant-Sperren mit einer Fairness-Richtlinie oder einem Timeout verwenden, um Thread-Mangel zu vermeiden. Sie können eine Thread-Fairness-Richtlinie anwenden. Es wird Ihnen helfen, einen Thread zu vermeiden, der für immer auf Ihre Ressourcen wartet.

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

Die “Fairness-Richtlinie” wählt den nächsten ausführbaren Thread aus. Es basiert auf Priorität, Zeit seit dem letzten Lauf, blah blah

Synchronize kann auch unbegrenzt blockieren, wenn es den Block nicht verlassen kann. Reentrantlock kann eine Zeitüberschreitung aufweisen.