Beachten Sie zunächst, dass die folgenden Codefragmente identisch sind.
public void foo() {
synchronized (this) {
}
}
und:
public synchronized void foo() {
}
mach genau das gleiche . Keine Präferenz für einen von ihnen außer der Lesbarkeit und dem Stil des Codes.
Wenn Sie Methoden oder Codeblöcke synchronisieren, ist es wichtig zu wissen, warum Sie so etwas tun und welches Objekt genau Sie sperren und zu welchem Zweck .
Beachten Sie auch, dass es Situationen gibt, in denen Sie clientseitige Codeblöcke synchronisieren möchten, in denen der von Ihnen angeforderte Monitor (dh das synchronisierte Objekt) nicht unbedingt this
wie in diesem Beispiel ist:
Vector v = getSomeGlobalVector();
synchronized (v) {
}
Ich schlage vor, Sie erhalten mehr Wissen über die gleichzeitige Programmierung. Sobald Sie genau wissen, was sich hinter den Kulissen abspielt, wird dies Ihnen sehr helfen. Sie sollten sich Concurrent Programming in Java ansehen , ein großartiges Buch zu diesem Thema. Wenn Sie schnell in das Thema eintauchen möchten, lesen Sie Java Concurrency @ Sun.
Wie frühere Antwortende festgestellt haben, ist es empfehlenswert, auf einem Objekt mit begrenztem Umfang zu synchronisieren (mit anderen Worten, wählen Sie den restriktivsten Bereich aus, mit dem Sie durchkommen können, und verwenden Sie diesen). Insbesondere ist das Synchronisieren auf
this
eine schlechte Idee, es sei denn Sie möchten den Benutzern Ihrer Klasse erlauben, die Sperre zu erhalten.Ein besonders hässlicher Fall tritt jedoch auf, wenn Sie sich für die Synchronisierung auf a entscheiden
java.lang.String
. Saiten können (und werden in der Praxis fast immer) interniert werden. Das bedeutet, dass sich jede Zeichenfolge mit gleichem Inhalt - in der GESAMTEN JVM - hinter den Kulissen als dieselbe Zeichenfolge herausstellt. Das bedeutet, dass, wenn Sie eine Zeichenfolge synchronisieren, ein anderer (völlig unterschiedlicher) Codeabschnitt, der auch eine Zeichenfolge mit demselben Inhalt sperrt, Ihren Code tatsächlich sperrt.Ich habe einmal einen Deadlock in einem Produktionssystem behoben und (sehr schmerzhaft) den Deadlock auf zwei völlig unterschiedliche Open Source-Pakete verfolgt, die jeweils auf einer Instanz von String synchronisiert wurden, deren Inhalt beide waren
"LOCK"
.quelle
Ich versuche, eine Synchronisierung zu vermeiden,
this
da dadurch jeder von außen, der einen Verweis auf dieses Objekt hatte, meine Synchronisierung blockieren kann. Stattdessen erstelle ich ein lokales Synchronisationsobjekt:public class Foo { private final Object syncObject = new Object(); … }
Jetzt kann ich dieses Objekt für die Synchronisation verwenden, ohne befürchten zu müssen, dass jemand das Schloss „stiehlt“.
quelle
private static class Lock { }; private final Object lock = new Lock();
.Nur um hervorzuheben, dass in Java auch ReadWriteLocks verfügbar sind, die als java.util.concurrent.locks.ReadWriteLock zu finden sind.
In den meisten Fällen trenne ich meine Sperren als "zum Lesen" und "für Updates". Wenn Sie einfach ein synchronisiertes Schlüsselwort verwenden, werden alle Lesevorgänge für denselben Methoden- / Codeblock in die Warteschlange gestellt. Es kann immer nur ein Thread gleichzeitig auf den Block zugreifen.
In den meisten Fällen müssen Sie sich keine Gedanken über Parallelitätsprobleme machen, wenn Sie nur lesen. Wenn Sie schreiben, machen Sie sich Sorgen über gleichzeitige Aktualisierungen (was zu Datenverlust führt) oder über das Lesen während eines Schreibvorgangs (teilweise Aktualisierungen), über die Sie sich Sorgen machen müssen.
Daher ist eine Lese- / Schreibsperre für mich bei der Multithread-Programmierung sinnvoller.
quelle
Sie möchten ein Objekt synchronisieren, das als Mutex dienen kann. Wenn die aktuelle Instanz ( diese Referenz) geeignet ist (z. B. kein Singleton), können Sie sie verwenden, da in Java jedes Objekt als Mutex dienen kann.
In anderen Fällen möchten Sie möglicherweise einen Mutex für mehrere Klassen freigeben, wenn Instanzen dieser Klassen möglicherweise alle Zugriff auf dieselben Ressourcen benötigen.
Dies hängt stark von der Umgebung ab, in der Sie arbeiten, und von der Art des Systems, das Sie erstellen. In den meisten Java EE-Anwendungen, die ich gesehen habe, besteht eigentlich kein wirklicher Synchronisationsbedarf ...
quelle
Persönlich denke ich, dass die Antworten, die darauf bestehen, dass die Synchronisierung nie oder nur selten richtig ist,
this
falsch sind. Ich denke, es hängt von Ihrer API ab. Wenn Ihre Klasse eine threadsichere Implementierung ist und Sie dies dokumentieren, sollten Sie verwendenthis
. Wenn durch die Synchronisierung nicht jede Instanz der Klasse als Ganzes beim Aufrufen ihrer öffentlichen Methoden threadsicher gemacht werden soll, sollten Sie ein privates internes Objekt verwenden. Wiederverwendbare Bibliothekskomponenten fallen häufig in die erstere Kategorie. Sie müssen sorgfältig überlegen, bevor Sie dem Benutzer nicht erlauben, Ihre API in eine externe Synchronisierung einzuschließen.Im ersteren Fall können mit using
this
mehrere Methoden atomar aufgerufen werden. Ein Beispiel ist PrintWriter, bei dem Sie möglicherweise mehrere Zeilen ausgeben möchten (z. B. eine Stapelverfolgung zur Konsole / zum Logger) und sicherstellen möchten, dass sie zusammen angezeigt werden. In diesem Fall ist die Tatsache, dass das Synchronisierungsobjekt intern ausgeblendet wird, ein echtes Problem. Ein weiteres Beispiel sind die synchronisierten Auflistungs-Wrapper. Dort müssen Sie das Auflistungsobjekt selbst synchronisieren, um iterieren zu können. Da die Iteration aus mehreren Methodenaufrufen besteht, können Sie sie intern nicht vollständig schützen.Im letzteren Fall verwende ich ein einfaches Objekt:
private Object mutex=new Object();
Nachdem ich jedoch viele JVM-Dumps und Stack-Traces gesehen habe, die besagen, dass eine Sperre "eine Instanz von java.lang.Object ()" ist, muss ich sagen, dass die Verwendung einer inneren Klasse oft hilfreicher ist, wie andere vorgeschlagen haben.
Jedenfalls sind das meine zwei Bits wert.
Bearbeiten: Eine andere Sache, wenn
this
ich synchronisiere, ziehe ich es vor, die Methoden zu synchronisieren und die Methoden sehr detailliert zu halten. Ich denke, es ist klarer und prägnanter.quelle
Bei der Synchronisierung in Java werden häufig Vorgänge auf derselben Instanz synchronisiert. Die Synchronisierung ist
this
dann sehr idiomatisch, dathis
es sich um eine gemeinsame Referenz handelt, die automatisch zwischen verschiedenen Instanzmethoden (oder Abschnitten von) in einer Klasse verfügbar ist.Die Verwendung einer anderen Referenz speziell zum Sperren, beispielsweise durch Deklarieren und Initialisieren eines privaten Felds
Object lock = new Object()
, habe ich nie benötigt oder verwendet. Ich denke, es ist nur nützlich, wenn Sie eine externe Synchronisation für zwei oder mehr nicht synchronisierte Ressourcen innerhalb eines Objekts benötigen, obwohl ich immer versuchen würde, eine solche Situation in eine einfachere Form umzugestalten.Auf jeden Fall wird implizit (synchronisierte Methode) oder explizit häufig
synchronized(this)
verwendet, auch in den Java-Bibliotheken. Es ist eine gute Redewendung und sollte, falls zutreffend, immer Ihre erste Wahl sein.quelle
Was Sie synchronisieren, hängt davon ab, welche anderen Threads, die möglicherweise mit diesem Methodenaufruf in Konflikt geraten, synchronisiert werden können.
Wenn
this
es sich um ein Objekt handelt, das nur von einem Thread verwendet wird, und wir auf ein veränderbares Objekt zugreifen, das von Threads gemeinsam genutzt wird, ist es ein guter Kandidat, über dieses Objekt zu synchronisieren. Die Synchronisierung aufthis
hat keinen Sinn, da ein anderer Thread, der dieses gemeinsam genutzte Objekt ändert, möglicherweise nicht einmal weißthis
, aber kennt das Objekt.Andererseits ist eine Synchronisierung
this
sinnvoll, wenn viele Threads gleichzeitig Methoden dieses Objekts aufrufen, z. B. wenn wir uns in einem Singleton befinden.Beachten Sie, dass eine synchronisierte Methode häufig nicht die beste Option ist, da wir die gesamte Zeit über eine Sperre halten. Wenn es zeitaufwändige, aber thread-sichere Teile und ein nicht so zeitaufwändiges thread-unsicheres Teil enthält, ist die Synchronisierung über die Methode sehr falsch.
quelle
Diese Deklaration synchronisiert die gesamte Methode.
private synchronized void doSomething() {
Diese Deklaration synchronisierte einen Teil des Codeblocks anstelle der gesamten Methode.
private void doSomething() { // thread-safe code synchronized(this) { // thread-unsafe code } // thread-safe code }
Von der Oracle - Dokumentation Seite
Das Synchronisieren dieser Methoden hat zwei Auswirkungen:
Erstens ist es nicht möglich, dass zwei Aufrufe synchronisierter Methoden für dasselbe Objekt verschachtelt werden . Wenn ein Thread eine synchronisierte Methode für ein Objekt ausführt, blockieren alle anderen Threads, die synchronisierte Methoden für denselben Objektblock aufrufen (Ausführung aussetzen), bis der erste Thread mit dem Objekt fertig ist.
Es gibt viele Möglichkeiten und Alternativen zur Synchronisation. Sie können Ihren Code-Thread sicher machen, indem Sie Parallelitäts- APIs auf hoher Ebene verwenden (verfügbar seit JDK 1.5).
Weitere Informationen finden Sie in den folgenden SE-Fragen:
Synchronisation gegen Sperre
Vermeiden Sie (dies) in Java synchronisiert?
quelle
Die Best Practices bestehen darin, ein Objekt zu erstellen, das ausschließlich die Sperre bereitstellt:
private final Object lock = new Object(); private void doSomething() { // thread-safe code synchronized(lock) { // thread-unsafe code } // thread-safe code }
Auf diese Weise können Sie sicher sein, dass kein aufrufender Code Ihre Methode jemals durch eine unbeabsichtigte
synchronized(yourObject)
Leitung blockieren kann .( Dank an @jared und @ yuval-adam, die dies oben ausführlicher erklärt haben. )
Ich vermute, dass die Popularität der Verwendung
this
in Tutorials von Sun javadoc stammt: https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.htmlquelle