Frage

ich habe gelesen Dieser Artikel Über "doppelt überprüftes Sperren" und aus dem Hauptthema des Artikels habe ich mich gefragt, warum der Autor irgendwann des Artikels die nächste Idiom verwendet:

Auflistung 7. Versuch, das Schreibproblem außerhalb der Reihenfolge zu lösen

public static Singleton getInstance()  
{
    if (instance == null)
    {
        synchronized(Singleton.class) {      //1
            Singleton inst = instance;         //2
            if (inst == null)
            {
                synchronized(Singleton.class) {  //3
                    inst = new Singleton();        //4
                }
                instance = inst;                 //5
            }
        }
    }
    return instance;
}

Und meine Frage ist: Gibt es einen Grund, zwei Code mit demselben Schloss zweimal zu synchronisieren? Haben Sie das einen Zweck?

Vielen Dank im Voraus.

War es hilfreich?

Lösung

Der Punkt des zweimals war versuchen Schreibvorgänge außerhalb der Ordnung zu verhindern. Das Speichermodell gibt an, wo Umbestände teilnehmen können, teilweise in Bezug auf Schlösser. Das Schloss stellt sicher, dass nach dem "Instance = Inst; Inst; Inst. Linie.

Um jedoch tiefer in das Thema einzutauchen, würde ich empfehlen Bill Pughs Artikel. Und dann versuchen Sie es nie :)

Andere Tipps

Der Artikel bezieht sich auf das Java-Speichermodell vor 5.0 (JMM). Unter diesem Modell schreibt ein synchronisierter Block erzwungen in das Hauptspeicher. Es scheint also ein Versuch zu sein, sicherzustellen, dass das Singleton -Objekt vor dem Hinweis darauf herausgedrückt wird. Es funktioniert jedoch nicht ganz, da das Schreiben zur Instanz in den Block verschoben werden kann - das Roach -Motel.

Das Modell vor 5.0 wurde jedoch nie korrekt implementiert. 1.4 sollte dem 5.0 -Modell folgen. Die Klassen werden träge initialisiert, also können Sie genauso gut einfach schreiben

public static final Singleton instance = new Singleton();

Oder besser, benutze keine Singletons, denn sie sind böse.

Jon Skeet hat Recht: Lesen Sie Bill Pughs Artikel. Die Idiom, die Hans verwendet Wird nicht funktionieren, und sollte nicht verwendet werden.

Dies ist unsicher:

private static Singleton instance;

public static Singleton getInstance() {
  if (instance == null) {
    synchronized(Singleton.class) {
      if (instance == null) {
        instance = new Singleton();
      }
    }
  }
  return instance;
}

Dies ist auch unsicher:

public static Singleton getInstance()  
{
    if (instance == null)
    {
        synchronized(Singleton.class) {      //1
            Singleton inst = instance;         //2
            if (inst == null)
            {
                synchronized(Singleton.class) {  //3
                    inst = new Singleton();        //4
                }
                instance = inst;                 //5
            }
        }
    }
    return instance;
}

Mach niemals einen von ihnen.

Synchronisieren Sie stattdessen die gesamte Methode:

    public static synchronized Singleton getInstance() {
      if (instance == null) {
        instance = new Singleton();
      }
      return instance;
    }

Wenn Sie dieses Objekt nicht in einer Sekunde abrufen, ist die Aufführung in realer Hinsicht vernachlässigbar.

Folgt dem John Skeet Empfehlung:

Um jedoch tiefer in das Thema zu gehen, würde ich Bill Pughs Artikel empfehlen. Und dann versuchen Sie es nie :)

Und hier ist der Schlüssel für den zweiten Synchronisierungsblock:

Dieser Code stellt das Helferobjekt in einen inneren synchronisierten Block auf. Die intuitive Idee hier ist, dass es an dem Punkt, an dem die Synchronisation freigesetzt wird, eine Speicherbarriere geben sollte und die Neuordnung der Initialisierung des Helferobjekts und der Zuordnung zum Feldhelfer verhindern sollte.

Im Grunde genommen versuchen wir mit dem inneren Synchronisierungsblock, die JMM zu "betrügen", die die Instanz im Synchronisationsblock erstellt, um die JMM zu zwingen, diese Zuordnung vor dem Synchronisierungsblock auszuführen. Das Problem hierfür ist jedoch, dass der JMM uns auf den Kopf stellt und das Assigment vor dem Synchronisierungsblock im Sync -Block verschiebt und unser Problem wieder in die Anfänger verschiebt.

Das habe ich aus diesen Artikeln verstanden, wirklich interessant und noch einmal danke für die Antworten.

Also gut, aber der Artikel sagte das

Der Code in Listing 7 funktioniert aufgrund der aktuellen Definition des Speichermodells nicht. Die JAVA -Sprachspezifikation (JLS) verlangt, dass der Code innerhalb eines synchronisierten Blocks nicht aus einem synchronisierten Block verschoben wird. Es heißt jedoch nicht, dass Code, das nicht in einem synchronisierten Block ist, nicht in einen synchronisierten Block verschoben werden kann.

Und scheint auch, als ob die JVM die nächste Übersetzung in "Pseudo-Code" in ASM macht:

public static Singleton getInstance()
{
  if (instance == null)
  {
    synchronized(Singleton.class) {      //1
      Singleton inst = instance;         //2
      if (inst == null)
      {
        synchronized(Singleton.class) {  //3
          //inst = new Singleton();      //4
          instance = new Singleton();               
        }
        //instance = inst;               //5
      }
    }
  }
  return instance;
}

Bisher ist der Punkt ohne Schreibvorgänge nach der "Instance = Inst" nicht erreicht?

Ich werde jetzt den Artikel lesen, danke für den Link.

Seit Java 5 können Sie doppelte Überprüfungsarbeiten durchführen, indem Sie das Feld volatil deklarieren.

Sehen http://www.cs.umd.edu/~pugh/java/Memorymodel/DoublCheckedLocking.html für eine vollständige Erklärung.

In Bezug auf diese Idiom gibt es einen sehr ratsamen und klärenden Artikel:

http://www.javarld.com/javarld/jw-02-2001/jw-0209-double.html?page=1

Auf der anderen Seite denke ich, was Dhighwayman.myopenid bedeutet, warum der Schriftsteller einen synchronisierten Block in einem anderen synchronisierten Block, der sich auf dieselbe Klasse bezieht, auf dieselbe Klasse (synchronisiert (synchronisiert (Singleton.Class))) eingestellt hat. Es kann als neue Instanz (Singleton Inst = Instance;) in diesem Block erstellt und garantiert, dass es thread ist, dass es notwendig ist, eine andere synchronisierte zu schreiben.

Ansonsten kann ich keinen Sinn sehen.

Sehen Sie sich das Google Tech -Vortrag auf dem Java -Speichermodell Für eine wirklich schöne Einführung in die Feinheiten des JMM. Da es hier fehlt, möchte ich auch auf Jeremy Mansons Blog hinweisen "Java Parallelität" Esp. der Beitrag auf Doppelte Locking (Jeder, der in der Java -Welt etwas ist, scheint einen Artikel dazu zu haben :).

Für Java 5 und besser gibt es tatsächlich eine doppelte Variante, die besser sein kann, als den gesamten Accessor zu synchronisieren. Dies wird auch in der erwähnt Doppelprüfung der Verriegelungserklärung :

class Foo {
    private volatile Helper helper = null;
    public Helper getHelper() {
        if (helper == null) {
            synchronized(this) {
                if (helper == null)
                    helper = new Helper();
            }
        }
        return helper;
    }
}

Der Hauptunterschied hier ist die Verwendung von flüchtig In der Variablenerklärung - ansonsten funktioniert es nicht und funktioniert sowieso nicht in Java 1.4 oder weniger.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top