it-swarm.com.de

Synchronisation vs. Sperre

Die Java.util.concurrent-API bietet eine Klasse mit dem Namen Lock, die das Steuerelement im Wesentlichen serialisiert, um auf die kritische Ressource zuzugreifen. Es gibt eine Methode wie park() und unpark()

Wir können ähnliche Dinge tun, wenn wir das synchronized-Schlüsselwort und die wait()- und notify() notifyAll()-Methoden verwenden können. 

Ich frage mich, welche davon in der Praxis besser ist und warum?

161
daydreamer

Wenn Sie nur ein Objekt sperren, würde ich lieber synchronized verwenden.

Beispiel:

Lock.acquire();
doSomethingNifty(); // Throws a NPE!
Lock.release(); // Oh noes, we never release the lock!

Sie müssen try{} finally{} überall explizit angeben.

Während mit synchronisiert, ist es sehr klar und unmöglich, falsch zu liegen:

synchronized(myObject) {
    doSomethingNifty();
}

Das heißt, Locks kann für kompliziertere Dinge nützlich sein, bei denen Sie nicht auf solch saubere Weise erwerben und freigeben können. Ich möchte ehrlich gesagt lieber keine nackten Locks verwenden, sondern nur eine anspruchsvollere Parallelitätssteuerung wie CyclicBarrier oder LinkedBlockingQueue, wenn sie Ihren Anforderungen entsprechen.

Ich hatte noch nie einen Grund, wait() oder notify() zu verwenden, aber es gibt einige gute Gründe.

166

Ich frage mich, welches davon in der Praxis besser ist und warum?

Ich habe festgestellt, dass Lock und Condition (und andere neue concurrent Klassen) nur weitere Tools für die Toolbox sind. Ich konnte fast alles, was ich brauchte, mit meinem alten Klauenhammer (dem Schlüsselwort synchronized) tun, aber es war in manchen Situationen umständlich. Einige dieser unangenehmen Situationen wurden viel einfacher, als ich meinen Werkzeugkasten mit weiteren Werkzeugen ausstattete: einem Gummihammer, einem Kugelhammer, einem Brecheisen und einigen Nagelstempeln. Allerdings sieht mein alter Klauenhammer immer noch seinen Teil der Verwendung.

Ich denke nicht, dass einer wirklich "besser" ist als der andere, sondern dass jeder besser für verschiedene Probleme geeignet ist. Kurz gesagt, das einfache Modell und der anwendungsbereichsbezogene Charakter von synchronized tragen zum Schutz vor Fehlern in meinem Code bei. Dieselben Vorteile sind jedoch in komplexeren Szenarien manchmal hinderlich. Es sind diese komplexeren Szenarien, für die das parallele Paket erstellt wurde, um die Lösung zu finden. Die Verwendung dieser übergeordneten Konstrukte erfordert jedoch eine explizitere und sorgfältigere Verwaltung des Codes.

===

Ich denke, dass JavaDoc die Unterscheidung zwischen Lock und synchronized gut beschreibt (die Betonung liegt bei mir):

Sperrenimplementierungen bieten umfangreichere Sperroperationen als mit synchronisierten Methoden und Anweisungen möglich sind. Sie erlauben eine flexiblere Strukturierung , können ganz andere Eigenschaften haben und können unterstützt mehrere verknüpfte Bedingungsobjekte .

...

Die Verwendung von synchronisierten Methoden oder Anweisungen ermöglicht den Zugriff auf die implizite Monitorsperre, die mit jedem Objekt verknüpft ist erzwingt die blockweise Erfassung und Freigabe aller Sperren : when Mehrere Sperren werden erworben sie müssen in umgekehrter Reihenfolge freigegeben werden und Alle Sperren müssen in demselben lexikalischen Umfang freigegeben werden, in dem sie erworben wurden .

Während der Scoping-Mechanismus für synchronisierte Methoden und Anweisungen erleichtert das Programmieren mit Monitorsperren erheblich und vermeidet häufig vorkommende Probleme Bei Programmierfehlern mit Sperren müssen Sie in einigen Fällen flexibler mit Sperren arbeiten. Zum Beispiel erfordern * * einige Algorithmen * zum Durchlaufen von Datenstrukturen, auf die gleichzeitig zugegriffen wird die Verwendung von "hand" -over-Hand "oder" Chain Locking ": Sie erwerben die Sperre von Knoten A, dann Knoten B, geben dann A frei und erwerben C, geben dann B frei und erwerben D und so weiter. Implementierungen der Lock-Schnittstelle ermöglichen die Verwendung solcher Techniken durch Ermöglichen, dass eine Sperre in verschiedenen Bereichen erworben und freigegeben wird und Ermöglichen, dass mehrere Sperren in beliebiger Reihenfolge erfasst und freigegeben werden.

Mit dieser erhöhten Flexibilität kommt zusätzliche Verantwortung . Das Fehlen einer blockstrukturierten Sperre beseitigt die automatische Freigabe von Sperren , die bei synchronisierten Methoden und Anweisungen auftreten . In den meisten Fällen sollte die folgende Redewendung verwendet werden:

...

Wenn das Sperren und Entsperren in verschiedenen Bereichen erfolgt , muss darauf geachtet werden, dass Sicherstellen, dass der gesamte Code ausgeführt wird, während die Sperre gehalten wird ist durch try-finally oder try-catch geschützt, um sicherzustellen, dass die Sperre aufgehoben wird wenn nötig.

Sperrenimplementierungen bieten zusätzliche Funktionalität gegenüber der Verwendung synchronisierter Methoden und Anweisungen, indem ein nicht blockierender Versuch, eine Sperre zu erlangen (tryLock ()), ein Versuch, erhalten die Sperre, die unterbrochen werden kann (lockInterruptibly (), und einen Versuch, erwerbe die Sperre, die eine Zeitüberschreitung verursachen kann (tryLock (long, TimeUnit)).

...

64
Bert F

Sie können alles erreichen, was die Dienstprogramme in Java.util.concurrent mit den einfachen Primitiven wie synchronized, volatile oder wait / notify erledigen.

Parallelität ist jedoch kompliziert und die meisten Leute bekommen zumindest einen Teil davon falsch, was dazu führt, dass der Code entweder falsch oder ineffizient ist (oder beides).

Die Concurrent-API bietet einen Ansatz auf höherer Ebene, der einfacher (und damit sicherer) zu verwenden ist. Kurz gesagt, Sie sollten synchronized, volatile, wait, notify nicht mehr direkt verwenden müssen. 

Die Klasse Lock selbst befindet sich auf der untergeordneten Seite dieser Toolbox. Möglicherweise müssen Sie sie auch nicht direkt verwenden (meistens können Sie Queues und Semaphore und so) verwenden. .

21
Thilo

Es gibt 4 Hauptgründe, warum Sie synchronized oder Java.util.concurrent.Lock verwenden möchten.

Hinweis: Synchronisiertes Sperren meine ich, wenn ich intrinsisches Sperren sage. 

  1. Als Java 5 mit ReentrantLocks herauskam, erwies es sich als sehr bemerkenswerter Durchsatz Dann intrinsisches Sperren. Wenn Sie auf der Suche nach einem schnelleren Verriegelungs-Mechanismus sind, müssen Sie j.u.c.ReentrantLock verwenden. Das intrinsische Sperren von Java 6 ist jetzt Vergleichbar.

  2. j.u.c.Lock hat verschiedene Mechanismen zum Verriegeln. Unterbrechbar sperren - versuchen, bis zur Sperre zu sperren Thread ist unterbrochen; zeitgesteuerte Sperre - versuchen, für einen bestimmten Betrag zu sperren Zeit und aufgeben, wenn Sie nicht gelingen; tryLock - Versuch zu sperren, wenn ein anderer Thread den .__ hält. Sperre aufgeben. Dies alles ist enthalten abgesehen von der einfachen Verriegelung. Die intrinsische Verriegelung bietet nur einfache Verriegelung

  3. Stil. Wenn sowohl 1 als auch 2 nicht fallen in Kategorien von dem, was du bist besorgt mit den meisten Menschen, einschließlich mir würde das .__ finden. intrinsische Sperrsematiken einfacher zu lesen und dann weniger ausführlich j.u.c.Lock-Verriegelung.
  4. Mehrere Bedingungen. Ein Objekt, für das Sie sich anmelden, kann nur benachrichtigt werden und wartet auf einen einzelnen Fall. Die NewCondition-Methode von Lock ermöglicht, dass ein -Einfach mehrere Gründe hat, um zu erwarten oder zu signalisieren. Ich brauche diese Funktion noch nicht in Üben, aber es ist eine schöne Funktion für diejenigen, die diese Funktion benötigen.
14
John Vint

Ich möchte noch einige weitere Dinge zu Bert F answer hinzufügen.

Locks unterstützt verschiedene Methoden zur feinstufigen Sperrensteuerung, die ausdrucksvoller sind als implizite Monitore (synchronized-Sperren)

Eine Sperre bietet exklusiven Zugriff auf eine gemeinsam genutzte Ressource: Nur jeweils ein Thread kann die Sperre erwerben, und für den gesamten Zugriff auf die gemeinsam genutzte Ressource muss die Sperre zuerst aufgerufen werden. Einige Sperren ermöglichen jedoch den gleichzeitigen Zugriff auf eine gemeinsam genutzte Ressource, beispielsweise die Lesesperre eines ReadWriteLock.

Vorteile von Lock over Synchronization aus Dokumentation Seite

  1. Die Verwendung synchronisierter Methoden oder Anweisungen ermöglicht den Zugriff auf die implizite Überwachungssperre, die jedem Objekt zugeordnet ist, erzwingt jedoch, dass die Erfassung und Freigabe aller Sperren blockweise erfolgt

  2. Sperrenimplementierungen bieten zusätzliche Funktionen über die Verwendung synchronisierter Methoden und Anweisungen, indem sie einen nicht blockierenden Versuch zum Abrufen einer lock (tryLock()), einen Versuch zum Abrufen der zu sperrenden Sperre (lockInterruptibly()) und einen Versuch zum Abrufen der Sperre, die timeout (tryLock(long, TimeUnit)) erhalten kann, bereitstellen.

  3. Eine Lock-Klasse kann auch Verhalten und Semantik bereitstellen, die sich von der impliziten Überwachungssperre deutlich unterscheidet, z. B. garantierte Reihenfolge, nicht wiedereintrittsfähige Verwendung oder Deadlock-Erkennung.

ReentrantLock : Vereinfacht ausgedrückt erlaubt ReentrantLock einem Objekt die erneute Eingabe von einem kritischen Abschnitt in einen anderen kritischen Abschnitt. Da Sie bereits über eine Sperre verfügen, um einen kritischen Abschnitt zu öffnen, können Sie andere kritische Abschnitte für dasselbe Objekt verwenden, indem Sie die aktuelle Sperre verwenden. 

ReentrantLock Hauptmerkmale gemäß diesem Artikel

  1. Fähigkeit, unterbrechungsfrei zu sperren.
  2. Zeitüberschreitung beim Warten auf die Sperre.
  3. Macht, ein faires Schloss zu schaffen.
  4. API zum Abrufen der Liste des wartenden Threads für die Sperre.
  5. Flexibilität zum Sperren ohne Sperren.

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

Abgesehen von diesen drei ReentrantLocks bietet Java 8 ein weiteres Lock

StampedLock:

Java 8 wird mit einer neuen Art von Sperre namens StampedLock geliefert, die ebenso wie im obigen Beispiel Lese- und Schreibsperren unterstützt. Im Gegensatz zu ReadWriteLock geben die Sperrmethoden eines StampedLock einen Stempel zurück, der durch einen langen Wert dargestellt wird. 

Mit diesen Stempeln können Sie entweder eine Sperre aufheben oder überprüfen, ob die Sperre noch gültig ist. Zusätzlich gestempelte Sperren unterstützen einen anderen Sperrmodus, der als optimistische Sperrung bezeichnet wird.

Schauen Sie sich diesen article über die Verwendung verschiedener Typen von ReentrantLock und StampedLock an.

5
Ravindra babu

Der Hauptunterschied besteht in der Fairness, dh Anfragen werden bearbeitet FIFO oder kann es zu Lastkämpfen kommen? Die Synchronisation auf Methodenebene gewährleistet eine faire oder FIFO Zuweisung der Sperre. Verwenden 

synchronized(foo) {
}

oder

lock.acquire(); .....lock.release();

gewährleistet nicht die Fairness.

Wenn Sie viele Konflikte um die Sperre haben, können Sie leicht auf Barging stoßen, wo neuere Anforderungen die Sperre bekommen und ältere Anfragen hängen bleiben. Ich habe Fälle gesehen, in denen 200 Threads in Kürze für eine Sperre eintreffen und der zweite ankommende als letzter verarbeitet wurde. Für manche Anwendungen ist das in Ordnung, für andere ist es tödlich.

Eine ausführliche Beschreibung dieses Themas finden Sie in Brian Goetzs Buch "Java Concurrency In Practice", Abschnitt 13.3.

4
Brian Tarbox

Brian Goetzs Buch "Java Concurrency In Practice", Abschnitt 13.3: "... Wie das Standard-ReentrantLock bietet das intrinsische Sperren keine deterministischen Fairness-Garantien, aber die Statistischen Fairness-Garantien der meisten Sperrimplementierungen sind für fast ausreichend alle Situationen ... "

3
bodrin

Lock macht Programmierern das Leben leichter. Hier sind einige Situationen, die mit dem Schloss leichter zu erreichen sind.

  1. Sperren Sie eine Methode und geben Sie die Sperre bei einer anderen Methode frei. 
  2. Sie haben zwei Threads, die an zwei verschiedenen Codeabschnitten arbeiten, jedoch ist der erste Thread davon abhängig, dass der zweite Thread einen bestimmten Codeabschnitt abschließt, bevor er weiter ausgeführt wird (während andere Threads gleichzeitig arbeiten). Eine gemeinsame Sperre kann dieses Problem ganz leicht lösen.
  3. Monitore implementieren. Zum Beispiel eine einfache Warteschlange, in der die put- und get-Methoden von vielen verschiedenen Threads ausgeführt werden. Möchten Sie jedoch nicht dieselben Methoden überlappen, können sich Put- und Get-Methoden nicht überschneiden. In diesem Fall erleichtert eine private Sperre das Leben.

Währenddessen sind die Sperre und die Bedingungen auf der Synchronisation aufgebaut. Sicher können Sie damit dasselbe Ziel erreichen. Dies kann jedoch Ihr Leben schwierig machen und kann Sie von der Lösung des tatsächlichen Problems abweichen. 

1

Hauptunterschied zwischen Sperren und Synchronisieren:

  • mit Sperren können Sie die Sperren in beliebiger Reihenfolge freigeben und abrufen.
  • mit synchronisiert können Sie die Sperren nur in der Reihenfolge freigeben, in der sie erworben wurden.
0
NKumar

Der Sperr- und Synchronisationsblock dient demselben Zweck, hängt jedoch von der Verwendung ab. Betrachten Sie den unteren Teil

void randomFunction(){
.
.
.
synchronize(this){
//do some functionality
}

.
.
.
synchronize(this)
{
// do some functionality
}


} // end of randomFunction

Wenn im obigen Fall ein Thread in den Synchronisierungsblock eintritt, ist der andere Block ebenfalls gesperrt. Wenn sich mehrere solcher Synchronisierungsblöcke auf demselben Objekt befinden, sind alle Blöcke gesperrt. In solchen Situationen kann Java.util.concurrent.Lock verwendet werden, um unerwünschtes Sperren von Blöcken zu verhindern

0
Shyam