it-swarm.com.de

Gibt es einen Vorteil, eine synchronisierte Methode anstelle eines synchronisierten Blocks zu verwenden?

Kann mir jemand den Vorteil der synchronisierten Methode gegenüber einem synchronisierten Block mit einem Beispiel erklären?

385
Warrior

Kann mir jemand mit einem Beispiel den Vorteil der synchronisierten Methode gegenüber dem synchronisierten Block erklären? Vielen Dank.

Die Verwendung einer synchronisierten Methode hat gegenüber dem Block keinen klaren Vorteil. 

Vielleicht ist die einzige (aber ich würde es nicht als Vorteil bezeichnen), dass Sie nicht die Objektreferenz this angeben müssen.

Methode:

public synchronized void method() { // blocks "this" from here.... 
    ...
    ...
    ...
} // to here

Block:

public void method() { 
    synchronized( this ) { // blocks "this" from here .... 
        ....
        ....
        ....
    }  // to here...
}

Sehen? Überhaupt kein Vorteil. 

Blöcke do haben jedoch Vorteile gegenüber Methoden, vor allem in der Flexibilität, da Sie ein anderes Objekt als Sperre verwenden können, während das Synchronisieren der Methode das gesamte Objekt sperren würde.

Vergleichen Sie: 

// locks the whole object
... 
private synchronized void someInputRelatedWork() {
    ... 
}
private synchronized void someOutputRelatedWork() {
    ... 
}

vs 

// Using specific locks
Object inputLock = new Object();
Object outputLock = new Object();

private void someInputRelatedWork() {
    synchronized(inputLock) { 
        ... 
    } 
}
private void someOutputRelatedWork() {
    synchronized(outputLock) { 
        ... 
    }
}

Wenn die Methode wächst, können Sie den synchronisierten Abschnitt dennoch getrennt halten:

 private void method() {
     ... code here
     ... code here
     ... code here
    synchronized( lock ) { 
        ... very few lines of code here
    }
     ... code here
     ... code here
     ... code here
     ... code here
}
413
OscarRyz

Der einzige wirkliche Unterschied besteht darin, dass ein synchronisierter Block auswählen kann, für welches Objekt er synchronisiert wird. Eine synchronisierte Methode kann nur 'this' (oder die entsprechende Klasseninstanz für eine synchronisierte Klassenmethode) verwenden. Zum Beispiel sind diese semantisch gleichwertig:

synchronized void foo() {
  ...
}

void foo() {
    synchronized (this) {
      ...
    }
}

Letzteres ist flexibler, da es um die zugehörige Sperre von any object (häufig eine Mitgliedsvariable) konkurrieren kann. Es ist auch detaillierter, weil gleichzeitig Code vor und nach dem Block ausgeführt werden kann, aber immer noch innerhalb der Methode. Natürlich können Sie eine synchronisierte Methode genauso gut verwenden, indem Sie den gleichzeitigen Code in separate, nicht synchronisierte Methoden umwandeln. Verwenden Sie das, was den Code verständlicher macht.

137
jcrossley3

Synchronisierte Methode

Pros:

  • Ihre IDE kann die synchronisierten Methoden angeben.
  • Die Syntax ist kompakter.
  • Zwingt das Aufteilen der synchronisierten Blöcke in getrennte Methoden.

Nachteile:

  • Synchronisiert darauf und ermöglicht es so auch Außenstehenden, darauf zu synchronisieren.
  • Es ist schwieriger, Code außerhalb des synchronisierten Blocks zu verschieben.

Synchronisierter Block

Pros:

  • Ermöglicht die Verwendung einer privaten Variablen für die Sperre, sodass die Sperre innerhalb der Klasse verbleiben muss.
  • Synchronisierte Blöcke können durch Suche nach Verweisen auf die Variable gefunden werden.

Nachteile:

  • Die Syntax ist komplizierter und macht das Lesen des Codes schwieriger.

Ich persönlich bevorzuge die Verwendung synchronisierter Methoden mit Klassen, die sich nur auf die Sache konzentrieren, die eine Synchronisierung erfordert. Eine solche Klasse sollte so klein wie möglich sein, sodass die Synchronisation leicht überprüft werden kann. Andere sollten sich nicht um die Synchronisation kümmern müssen.

75
iny

Der Hauptunterschied besteht darin, dass Sie, wenn Sie einen synchronisierten Block verwenden, ein anderes Objekt als this sperren können, wodurch es wesentlich flexibler wird.

Angenommen, Sie haben eine Nachrichtenwarteschlange und mehrere Nachrichtenproduzenten und -konsumenten. Wir wollen nicht, dass sich Produzenten gegenseitig beeinflussen, aber die Konsumenten sollten in der Lage sein, Nachrichten abzurufen, ohne auf die Produzenten warten zu müssen. Wir erstellen also einfach ein Objekt

Object writeLock = new Object();

Und jedes Mal, wenn ein Produzent eine neue Nachricht hinzufügen möchte, sperren wir das einfach:

synchronized(writeLock){
  // do something
}

Die Verbraucher können also immer noch lesen und die Produzenten werden gesperrt.

35
cdecker

Synchronisierte Methode

Synchronisierte Methoden haben zwei Auswirkungen.
Wenn ein Thread eine synchronisierte Methode für ein Objekt ausführt, werden zunächst alle anderen Threads, die synchronisierte Methoden für dasselbe Objekt aufrufen, blockiert (Suspend-Ausführung), bis der erste Thread mit dem Objekt fertig ist. 

Zweitens, wenn eine synchronisierte Methode beendet wird, wird automatisch eine Ereignis-vor-Beziehung mit einem nachfolgenden Aufruf einer synchronisierten Methode für dasselbe Objekt hergestellt. Dies garantiert, dass Änderungen des Status des Objekts für alle Threads sichtbar sind. 

Beachten Sie, dass Konstruktoren nicht synchronisiert werden können - die Verwendung des synchronisierten Schlüsselworts mit einem Konstruktor ist ein Syntaxfehler. Das Synchronisieren von Konstruktoren ist nicht sinnvoll, da nur der Thread, der ein Objekt erstellt, während der Erstellung Zugriff darauf haben sollte. 

Synchronisierte Anweisung

Im Gegensatz zu synchronisierten Methoden müssen synchronisierte Anweisungen das Objekt angeben, das die intrinsische Sperre bereitstellt: Meistens verwende ich diese Option, um den Zugriff auf eine Liste oder Map zu synchronisieren. Ich möchte jedoch nicht den Zugriff auf alle Methoden des Objekts blockieren. 

F: Intrinsic Locks und Synchronisation .__ Die Synchronisierung basiert auf einer internen Entität, die als intrinsische Sperre oder Monitorsperre bezeichnet wird. (In der API-Spezifikation wird diese Entität oft nur als "Monitor" bezeichnet.) Intrinsische Sperren spielen bei beiden Aspekten der Synchronisierung eine Rolle: Erzwingen des ausschließlichen Zugriffs auf den Status eines Objekts und Herstellen von Geschehnissen, die für die Sichtbarkeit wesentlich sind. 

Jedem Objekt ist eine intrinsische Sperre zugeordnet. Konventionell muss ein Thread, der exklusiven und konsistenten Zugriff auf die Felder eines Objekts benötigt, die intrinsische Sperre des Objekts erwerben, bevor auf sie zugegriffen wird, und dann die intrinsische Sperre aufheben, wenn er damit fertig ist. Von einem Thread wird gesagt, dass er die intrinsische Sperre zwischen dem Zeitpunkt besitzt, zu dem er die Sperre erlangt hat, und die Sperre aufgehoben hat. Solange ein Thread eine intrinsische Sperre besitzt, kann kein anderer Thread dieselbe Sperre erwerben. Der andere Thread wird blockiert, wenn er versucht, die Sperre abzurufen. 

package test;

public class SynchTest implements Runnable {  
    private int c = 0;

    public static void main(String[] args) {
        new SynchTest().test();
    }

    public void test() {
        // Create the object with the run() method
        Runnable runnable = new SynchTest();
        Runnable runnable2 = new SynchTest();
        // Create the thread supplying it with the runnable object
        Thread thread = new Thread(runnable,"thread-1");
        Thread thread2 = new Thread(runnable,"thread-2");
//      Here the key point is passing same object, if you pass runnable2 for thread2,
//      then its not applicable for synchronization test and that wont give expected
//      output Synchronization method means "it is not possible for two invocations
//      of synchronized methods on the same object to interleave"

        // Start the thread
        thread.start();
        thread2.start();
    }

    public synchronized  void increment() {
        System.out.println("Begin thread " + Thread.currentThread().getName());
        System.out.println(this.hashCode() + "Value of C = " + c);
//      If we uncomment this for synchronized block, then the result would be different
//      synchronized(this) {
            for (int i = 0; i < 9999999; i++) {
                c += i;
            }
//      }
        System.out.println("End thread " + Thread.currentThread().getName());
    }

//    public synchronized void decrement() {
//        System.out.println("Decrement " + Thread.currentThread().getName());
//    }

    public int value() {
        return c;
    }

    @Override
    public void run() {
        this.increment();
    }
}

Überprüfung verschiedener Ausgänge mit synchronisierter Methode, Blockierung und ohne Synchronisation.

29
sudheer

Hinweis: statische synchronisierte Methoden und Blöcke funktionieren mit dem Class-Objekt.

public class MyClass {
   // locks MyClass.class
   public static synchronized void foo() {
// do something
   }

   // similar
   public static void foo() {
      synchronized(MyClass.class) {
// do something
      }
   }
}
27
Peter Lawrey

Wenn der Java-Compiler Ihren Quellcode in Byte-Code konvertiert, behandelt er synchronisierte Methoden und synchronisierte Blöcke sehr unterschiedlich.

Wenn die JVM eine synchronisierte Methode ausführt, gibt der ausführende Thread an, dass in der method_info-Struktur der Methode das ACC_SYNCHRONIZED-Flag gesetzt ist. Anschließend wird die Sperre des Objekts automatisch abgerufen, die Methode aufgerufen und die Sperre aufgehoben. Wenn eine Ausnahme auftritt, gibt der Thread die Sperre automatisch frei.

Beim Synchronisieren eines Methodenblocks wird die integrierte JVM-Unterstützung für das Abrufen der Sperren- und Ausnahmebehandlung eines Objekts umgangen, und die Funktionalität muss explizit in Bytecode geschrieben werden. Wenn Sie den Bytecode für eine Methode mit einem synchronisierten Block lesen, werden mehr als ein Dutzend zusätzlicher Operationen zur Verwaltung dieser Funktionalität angezeigt. 

Dies zeigt Aufrufe zum Generieren einer synchronisierten Methode und eines synchronisierten Blocks:

public class SynchronizationExample {
    private int i;

    public synchronized int synchronizedMethodGet() {
        return i;
    }

    public int synchronizedBlockGet() {
        synchronized( this ) {
            return i;
        }
    }
}

Die synchronizedMethodGet()-Methode generiert den folgenden Bytecode:

0:  aload_0
1:  getfield
2:  nop
3:  iconst_m1
4:  ireturn

Und hier ist der Bytecode aus der synchronizedBlockGet()-Methode:

0:  aload_0
1:  dup
2:  astore_1
3:  monitorenter
4:  aload_0
5:  getfield
6:  nop
7:  iconst_m1
8:  aload_1
9:  monitorexit
10: ireturn
11: astore_2
12: aload_1
13: monitorexit
14: aload_2
15: athrow

Ein wesentlicher Unterschied zwischen synchronisierten Methoden und Blöcken besteht darin, dass Synchronisierte Blöcken im Allgemeinen den Sperrbereich reduzieren. Da der Sperrbereich umgekehrt proportional zur Leistung ist, ist es immer besser, nur kritische Codeabschnitte zu sperren. Eines der besten Beispiele für die Verwendung von synchronisierten Blöcken ist doppelt gesperrtes Sperren im Singleton-Muster wobei anstelle des Sperrens der gesamten getInstance()-Methode nur kritische Codeabschnitte gesperrt werden, die zum Erstellen der Singleton-Instanz verwendet werden. Dies verbessert die Leistung drastisch, da das Sperren nur ein- oder zweimal erforderlich ist.

Bei der Verwendung synchronisierter Methoden müssen Sie besonders vorsichtig sein, wenn Sie sowohl statisch synchronisierte als auch nicht statische synchronisierte Methoden kombinieren.

17
Mohammad Adil

Meistens verwende ich dies, um den Zugriff auf eine Liste oder Map zu synchronisieren, aber ich möchte nicht den Zugriff auf alle Methoden des Objekts blockieren.

Im folgenden Code blockiert ein Thread, der die Liste ändert, nicht, auf einen Thread zu warten, der die Map ändert. Wenn die Methoden für das Objekt synchronisiert würden, müsste jede Methode warten, auch wenn die von ihnen vorgenommenen Änderungen keinen Konflikt verursachen.

private List<Foo> myList = new ArrayList<Foo>();
private Map<String,Bar) myMap = new HashMap<String,Bar>();

public void put( String s, Bar b ) {
  synchronized( myMap ) {
    myMap.put( s,b );
    // then some thing that may take a while like a database access or RPC or notifying listeners
  }
}

public void hasKey( String s, ) {
  synchronized( myMap ) {
    myMap.hasKey( s );
  }
}

public void add( Foo f ) {
  synchronized( myList ) {
    myList.add( f );
// then some thing that may take a while like a database access or RPC or notifying listeners
  }
}

public Thing getMedianFoo() {
  Foo med = null;
  synchronized( myList ) {
    Collections.sort(myList);
    med = myList.get(myList.size()/2); 
  }
  return med;
}
12
Clint

Mit synchronisierten Blöcken können Sie mehrere Synchronisierer verwenden, so dass mehrere gleichzeitige, jedoch nicht konfliktträchtige Dinge gleichzeitig ausgeführt werden können.

6
Paul Tomblin

Synchronisierte Methoden können mithilfe der Reflection-API überprüft werden. Dies kann nützlich sein, um einige Verträge zu testen, z. B. alle Methoden im Modell werden synchronisiert .

Das folgende Snippet druckt alle synchronisierten Methoden von Hashtable:

for (Method m : Hashtable.class.getMethods()) {
        if (Modifier.isSynchronized(m.getModifiers())) {
            System.out.println(m);
        }
}
6
Kojotak

Die Verwendung einer Sperre auf Methodenebene ist oft zu unhöflich. Warum ein Code blockieren, der nicht auf gemeinsam genutzte Ressourcen zugreift, indem eine gesamte Methode gesperrt wird? Da jedes Objekt über eine Sperre verfügt, können Sie Dummy-Objekte erstellen, um die Synchronisierung auf Blockebene zu implementieren Die Blockebene ist effizienter, da die gesamte Methode nicht gesperrt wird.

Hier ein Beispiel

Methodenebene

class MethodLevel {

  //shared among threads
SharedResource x, y ;

public void synchronized method1() {
   //multiple threads can't access
}
public void synchronized method2() {
  //multiple threads can't access
}

 public void method3() {
  //not synchronized
  //multiple threads can access
 }
}

Blockebene

class BlockLevel {
  //shared among threads
  SharedResource x, y ;

  //dummy objects for locking
  Object xLock = new Object();
  Object yLock = new Object();

    public void method1() {
     synchronized(xLock){
    //access x here. thread safe
    }

    //do something here but don't use SharedResource x, y
    // because will not be thread-safe
     synchronized(xLock) {
       synchronized(yLock) {
      //access x,y here. thread safe
      }
     }

     //do something here but don't use SharedResource x, y
     //because will not be thread-safe
    }//end of method1
 }

[Bearbeiten]

Für Collection wie Vector und Hashtable werden sie synchronisiert, wenn ArrayList oder HashMap nicht sind und Sie ein synchronisiertes Schlüsselwort setzen oder die Collections-Methode synchronisieren müssen:

Map myMap = Collections.synchronizedMap (myMap); // single lock for the entire map
List myList = Collections.synchronizedList (myList); // single lock for the entire list
5
Maxim Shoustin

Wichtiger Hinweis zur Verwendung des synchronisierten Blocks: Vorsicht, was Sie als Sperrobjekt verwenden!

Das obige Code-Snippet von user2277816 veranschaulicht diesen Punkt dadurch, dass ein Verweis auf ein String-Literal als Sperrobjekt verwendet wird .. _ Beachten Sie, dass String-Literale automatisch in Java interniert sind und Sie das Problem zunächst sehen sollten: jedes Codeteil, das synchronisiert wird auf der wörtlichen "Sperre", teilt die gleiche Sperre! Dies kann leicht zu Deadlocks führen, bei denen der Code völlig unabhängig ist.

Es sind nicht nur String-Objekte, mit denen Sie vorsichtig sein müssen. Boxed Primitive sind ebenfalls eine Gefahr, da Autoboxing und die valueOf-Methoden dieselben Objekte je nach Wert wiederverwenden können.

Weitere Informationen finden Sie unter: https://www.securecoding.cert.org/confluence/display/Java/LCK01-J.+Do+not+synchronize+on+objects+that+may+be+recycling

5
Søren Boisen

Der einzige Unterschied: Synchronisierte Blöcke ermöglichen im Gegensatz zu synchronisierten Methoden ein granulares Sperren

Grundsätzlich wurden synchronized-Block oder -Methoden verwendet, um sicheren Code für den Thread zu schreiben, indem Speicherfehler vermieden werden. 

Diese Frage ist sehr alt und viele Dinge haben sich in den letzten 7 Jahren geändert. Für die Thread-Sicherheit wurden neue Programmierkonstrukte eingeführt.

Sie können Thread-Sicherheit erreichen, indem Sie eine erweiterte Parallelitäts-API anstelle von synchronied-Blöcken verwenden. Diese Dokumentation page enthält gute Programmierkonstrukte, um Threadsicherheit zu erreichen.

Objekte sperren Unterstützung für Idiome, die viele gleichzeitige Anwendungen vereinfachen.

Executors Definieren Sie eine übergeordnete API zum Starten und Verwalten von Threads. Implementierungsimplementierungen von Java.util.concurrent bieten eine Thread-Pool-Verwaltung, die sich für große Anwendungen eignet.

Concurrent Collections erleichtern die Verwaltung großer Datensammlungen und können die Synchronisierung erheblich reduzieren.

Atomic Variables verfügen über Funktionen, die die Synchronisierung minimieren und zur Vermeidung von Speicherkonsistenzfehlern beitragen.

ThreadLocalRandom (in JDK 7) ermöglicht die effiziente Erzeugung von Pseudozufallszahlen aus mehreren Threads.

Besserer Ersatz für Synchronized ist ReentrantLock , das Lock-API verwendet

Ein wiedereintrittsfähiger beiderseitiger Ausschluss Sperren mit demselben grundlegenden Verhalten und derselben Semantik wie die implizite Überwachungssperre, auf die mit synchronisierten Methoden und Anweisungen zugegriffen wird, jedoch mit erweiterten Funktionen.

Beispiel mit Schlössern:

class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }

Informationen zu anderen Programmierkonstrukten finden Sie auch in Java.util.concurrent und Java.util.concurrent.atomic

Siehe auch diese verwandte Frage:

Synchronisation vs. Sperre

5
Ravindra babu

Die synchronisierte Methode wird zum Sperren aller Objekte verwendet... Der synchronisierte Block wird zum Sperren bestimmter Objekte verwendet

4
kishore

Im Allgemeinen handelt es sich dabei meist um die gleichen Informationen, außer dass sie explizit den verwendeten Monitor des Objekts gegenüber dem impliziten Objekt angeben. Ein Nachteil synchronisierter Methoden, den ich manchmal übersehen habe, ist, dass durch die Verwendung der Referenz "this" zur Synchronisierung die Möglichkeit besteht, dass externe Objekte dasselbe Objekt blockieren. Das kann ein sehr subtiler Fehler sein, wenn Sie darauf stoßen. Durch die Synchronisierung eines internen expliziten Objekts oder eines anderen vorhandenen Felds kann dieses Problem vermieden werden, indem die Synchronisation vollständig gekapselt wird.

3
Alex Miller

Bei synchronisierten Methoden wird die Sperre für ein Objekt übernommen. Wenn Sie jedoch einen synchronisierten Block verwenden, haben Sie die Möglichkeit, ein Objekt anzugeben, für das die Sperre erworben werden soll.

Beispiel:

    Class Example {
    String test = "abc";
    // lock will be acquired on String  test object.
    synchronized (test) {
        // do something
    }

   lock will be acquired on Example Object
   public synchronized void testMethod() {
     // do some thing
   } 

   }
2
Srinu Yarru

Ich weiß, dass dies eine alte Frage ist, aber da ich die Antworten hier schnell durchgelesen habe, habe ich nicht wirklich gesehen, dass irgendjemand erwähnt, dass manchmal eine synchronized-Methode die Sperre wrong sein kann.
Von Java Parallelität in der Praxis (S. 72):

public class ListHelper<E> {
  public List<E> list = Collections.syncrhonizedList(new ArrayList<>());
...

public syncrhonized boolean putIfAbsent(E x) {
 boolean absent = !list.contains(x);
if(absent) {
 list.add(x);
}
return absent;
}

Der obige Code hat das Aussehen, dass er Thread-sicher ist. In der Realität ist es jedoch nicht. In diesem Fall wird die Sperre für die Instanz der Klasse erhalten. Es ist jedoch möglich, dass list von einem anderen Thread geändert wird, der diese Methode nicht verwendet. Der richtige Ansatz wäre zu verwenden 

public boolean putIfAbsent(E x) {
 synchronized(list) {
  boolean absent = !list.contains(x);
  if(absent) {
    list.add(x);
  }
  return absent;
}
}

Der obige Code würde alle Threads, die versuchen, list zu ändern, daran hindern, die Liste zu ändern, bis der synchronisierte Block abgeschlossen ist.

2
aarbor

Wie bereits erwähnt, kann der synchronisierte Block eine benutzerdefinierte Variable als Sperrobjekt verwenden, wenn die synchronisierte Funktion nur "dieses" verwendet. Und natürlich können Sie mit Bereichen Ihrer Funktion, die synchronisiert werden sollen, manipulieren ... Aber jeder sagt, dass kein Unterschied zwischen synchronisierter Funktion und Block besteht, der die gesamte Funktion mit "this" als Sperrobjekt abdeckt. Das stimmt nicht, der Unterschied liegt im Bytecode, der in beiden Situationen erzeugt wird. Bei synchronisierter Blocknutzung sollte die lokale Variable zugewiesen werden, die auf "this" verweist. Als Ergebnis haben wir eine etwas größere Größe für die Funktion (nicht relevant, wenn Sie nur wenige Funktionen haben).

Eine ausführlichere Erklärung des Unterschieds finden Sie hier: http://www.artima.com/insidejvm/ed2/threadsynchP.html

2
Roman

Praktisch ist der Vorteil synchronisierter Methoden gegenüber synchronisierten Blöcken, dass sie idiotresistenter sind. Da Sie kein beliebiges Objekt zum Sperren auswählen können, können Sie die synchronisierte Methodensyntax nicht dazu missbrauchen, dumme Dinge wie das Sperren eines Zeichenfolgenlitals oder das Sperren des Inhalts eines veränderlichen Felds auszuführen, das unter den Threads geändert wird.

Andererseits können Sie mit synchronisierten Methoden die Sperre nicht vor einem Aufruf eines Threads schützen, der einen Verweis auf das Objekt erhalten kann.

Die Verwendung von synchronisierten Methoden als Modifizierer ist daher besser für den Schutz Ihrer Cow-Orkers vor Verletzungen, während die Verwendung synchronisierter Blöcke in Verbindung mit privaten endgültigen Sperrobjekten den eigenen Code besser vor den Cow-Orkern schützt.

Aus einer Java-Spezifikationszusammenfassung: http://www.cs.cornell.edu/andru/javaspec/17.doc.html

Die synchronisierte Anweisung (§14.17) berechnet einen Verweis auf ein Objekt; Es versucht dann, eine Sperraktion für dieses Objekt auszuführen, und ___ nicht. Fahren Sie fort, bis die Sperraktion erfolgreich abgeschlossen wurde. ...

Eine synchronisierte Methode (§8.4.3.5) führt automatisch eine Sperraktion aus wenn es aufgerufen wird; Sein Hauptteil wird erst ausgeführt, wenn die Sperraktion .__ hat. erfolgreich abgeschlossen. Wenn die Methode eine Instanzmethode ist, dann sperrt die Sperre, die der Instanz zugeordnet ist, für die sie aufgerufen wurde (dh das Objekt, das während der Ausführung von des Rumpfes der Methode als dieses bekannt wird). Wenn die Methode statisch ist, sperrt sie die Sperre, die dem Klassenobjekt zugeordnet ist, das die Klasse in .__ darstellt. welche Methode ist definiert. ...

Basierend auf diesen Beschreibungen würde ich sagen, dass die meisten früheren Antworten korrekt sind, und eine synchronisierte Methode kann besonders für statische Methoden nützlich sein, bei denen Sie sonst herausfinden müssten, wie Sie das "Class-Objekt" erhalten, das die Klasse darstellt, in der sich die Methode befand definiert. "

Edit: Ich dachte ursprünglich, dass dies Zitate der aktuellen Java-Spezifikation sind. Es wurde klargestellt, dass diese Seite nur eine Zusammenfassung/Erklärung der Spezifikation ist

1
Josiah Yoder

TLDR; Verwenden Sie weder den Modifizierer synchronized noch den Ausdruck synchronized(this){...}, sondern synchronized(myLock){...}, wobei myLock ein abschließendes Instanzfeld ist, das ein privates Objekt enthält.


Der Unterschied zwischen der Verwendung des Modifizierers synchronized in der Methodendeklaration und dem Ausdruck synchronized(..){ } im Methodenkörper ist folgender:

  • Der synchronized-Modifizierer, der in der Signatur der Methode angegeben ist
    1. ist im generierten JavaDoc sichtbar, 
    2. ist programmatisch bestimmbar über Reflection beim Testen des Modifizierers einer Methode für Modifier.SYNCHRONIZED ,
    3. erfordert weniger Tippen und Eindrücke im Vergleich zu synchronized(this) { .... } und
    4. (abhängig von Ihrer IDE) ist in der Klassengliederung und Code-Vervollständigung sichtbar,
    5. verwendet das this-Objekt als Sperre, wenn es für eine nicht statische Methode deklariert wird, oder die umgebende Klasse, wenn es für eine statische Methode deklariert wird.
  • Mit dem Ausdruck synchronized(...){...} können Sie
    1. nur die Ausführung von Teilen des Methodenkörpers zu synchronisieren,
    2. innerhalb eines Konstruktors oder eines ( static ) Initialisierungsblocks verwendet werden,
    3. um das Sperrobjekt auszuwählen, das den synchronisierten Zugriff steuert.

Die Verwendung des Modifizierers synchronized oder synchronized(...) {...} mit this als Sperrobjekt (wie in synchronized(this) {...}) hat jedoch den gleichen Nachteil. Beide verwenden ihre eigene Instanz als Sperrobjekt für die Synchronisierung. Dies ist gefährlich, da nicht nur das Objekt selbst, sondern auch any anderes externes Objekt/Code, der einen Verweis auf dieses Objekt enthält, es als Synchronisationssperre mit potenziell schwerwiegenden Nebenwirkungen verwenden kann (Leistungsabfall und Deadlocks ). .

Daher empfiehlt es sich, weder den Modifizierer synchronized noch den Ausdruck synchronized(...) in Verbindung mit this als Sperrobjekt zu verwenden, sondern ein Sperrobjekt, das für dieses Objekt privat ist. Zum Beispiel:

public class MyService {
    private final lock = new Object();

    public void doThis() {
       synchronized(lock) {
          // do code that requires synchronous execution
        }
    }

    public void doThat() {
       synchronized(lock) {
          // do code that requires synchronous execution
        }
    }
}

Sie können auch mehrere Sperrobjekte verwenden. Es muss jedoch besonders darauf geachtet werden, dass dies nicht zu Deadlocks führt, wenn sie verschachtelt verwendet werden.

public class MyService {
    private final lock1 = new Object();
    private final lock2 = new Object();

    public void doThis() {
       synchronized(lock1) {
          synchronized(lock2) {
              // code here is guaranteed not to be executes at the same time
              // as the synchronized code in doThat() and doMore().
          }
    }

    public void doThat() {
       synchronized(lock1) {
              // code here is guaranteed not to be executes at the same time
              // as the synchronized code in doThis().
              // doMore() may execute concurrently
        }
    }

    public void doMore() {
       synchronized(lock2) {
              // code here is guaranteed not to be executes at the same time
              // as the synchronized code in doThis().
              // doThat() may execute concurrently
        }
    }
}
1

Ich nehme an, diese Frage bezieht sich auf den Unterschied zwischen Thread Safe Singleton und Lazy-Initialisierung mit Double Check-Locking. Ich beziehe mich immer auf diesen Artikel, wenn ich ein bestimmtes Singleton implementieren muss.

Nun, dies ist ein Thread Safe Singleton:

// Java program to create Thread Safe 
// Singleton class 
public class GFG  
{ 
  // private instance, so that it can be 
  // accessed by only by getInstance() method 
  private static GFG instance; 

  private GFG()  
  { 
    // private constructor 
  } 

 //synchronized method to control simultaneous access 
  synchronized public static GFG getInstance()  
  { 
    if (instance == null)  
    { 
      // if instance is null, initialize 
      instance = new GFG(); 
    } 
    return instance; 
  } 
} 

Pros:

  1. Eine faule Initialisierung ist möglich.

  2. Es ist fadensicher.

Nachteile:

  1. die getInstance () -Methode wird synchronisiert, sodass die Leistung beeinträchtigt wird, da mehrere Threads nicht gleichzeitig darauf zugreifen können.

Dies ist eine Lazy-Initialisierung mit Double-Check-Sperren:

// Java code to explain double check locking 
public class GFG  
{ 
  // private instance, so that it can be 
  // accessed by only by getInstance() method 
  private static GFG instance; 

  private GFG()  
  { 
    // private constructor 
  } 

  public static GFG getInstance() 
  { 
    if (instance == null)  
    { 
      //synchronized block to remove overhead 
      synchronized (GFG.class) 
      { 
        if(instance==null) 
        { 
          // if instance is null, initialize 
          instance = new GFG(); 
        } 

      } 
    } 
    return instance; 
  } 
} 

Pros:

  1. Eine faule Initialisierung ist möglich.

  2. Es ist auch Thread-sicher.

  3. Die Leistung aufgrund eines synchronisierten Schlüsselworts wurde reduziert.

Nachteile:

  1. Beim ersten Mal kann die Leistung beeinträchtigt werden.

  2. Wie Nachteile der doppelten Check-Verriegelungsmethode ist erträglich, so dass es sein kann Wird für Hochleistungs-Multithread-Anwendungen verwendet.

Weitere Informationen finden Sie in diesem Artikel:

https://www.geeksforgeeks.org/Java-singleton-design-pattern-practices-examples/

0
Alexandre Lage