Frage

Java nur 5 und höher. Nehmen Sie einen Multiprozessor-Shared-Memory-Computer an (Sie verwenden wahrscheinlich jetzt einen).

Hier ist ein Code für die faule Initialisierung eines Singleton:

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

Tut instance müssen deklariert werden volatile Um zu verhindern, dass der Optimierer GetInstance () wie folgt umschreibt (was in einem sequentiellen Programm korrekt wäre):

public static MySingleton getInstance() {
  if (instance == null) {
    synchronized (MySingleton.class) {
      // instance must be null or we wouldn't be here  (WRONG!)
      instance = new MySingleton();
    }
  }
}

Angenommen, der Optimierer schreibt den Code nicht um, wenn instance wird nicht deklariert volatile Ist es immer noch garantiert, in den Speicher zu werden, wenn der synchronized Block wird beendet und aus dem Speicher gelesen, wenn die synchronized Block ist eingegeben?

Bearbeiten: Ich habe vergessen, GetInstance () statisch zu machen. Ich glaube nicht, dass das die Gültigkeit der Antworten verändert. Sie alle wussten, was ich meinte.

War es hilfreich?

Lösung

Ja, instance sollte deklariert werden volatile. Selbst dann wird empfohlen, keine doppelte Überprüfung zu verwenden. Es hatte früher einen schwerwiegenden Fehler, der die Veröffentlichung teilweise implementierter Objekte ermöglichte, um genau das Java -Speichermodell zu sein. Dies wurde in Java5 fix Lazy Initialisierungshalter Idiom stattdessen.

Aus Java Parallelität in der Praxis, Abschnitt 16.2:

Das eigentliche Problem mit DCL ist die Annahme, dass das Schlimmste, was beim Lesen einer gemeinsam genutzten Objektreferenz ohne Synchronisation passieren kann null); In diesem Fall kompensiert die DCL -Idiom dieses Risiko, indem es erneut versucht, mit dem Schloss gehalten zu werden. Der schlimmste Fall ist jedoch tatsächlich erheblich schlechter - es ist möglich, einen aktuellen Wert der Referenz zu erkennen, aber abgestandene Werte für den Zustand des Objekts, was bedeutet, dass sich das Objekt in einem ungültigen oder falschen Zustand befindet.

Nachfolgende Änderungen in der JMM (Java 5.0 und später) haben DCL ermöglicht, zu arbeiten wenn resource wird gemacht volatile, und die Leistungsauswirkungen davon ist seitdem gering volatile Lesevorgänge sind normalerweise nur etwas teurer als nichtflüchtige Lesevorgänge. Dies ist jedoch eine Redewendung, deren Nützlichkeit weitgehend vergangen ist - die Kräfte, die sie motiviert haben (langsame nicht inpassende Synchronisation, langsames JVM -Startup), sind nicht mehr im Spiel, was es als Optimierung weniger effektiv macht. Der idiom in der faulen Initialisierungsinhaber bietet die gleichen Vorteile und ist leichter zu verstehen.

Andere Tipps

Ja, die Instanz muss mit doppelter Überprüfung in Java volatil sein, da ansonsten die Initialisierung von Mysingleton ein teilweise konstruiertes Objekt dem Rest des Systems aussetzen könnte. Es ist auch wahr, dass die Threads synchronisieren, wenn sie die "synchronisierte" Aussage erreichen, aber das ist in diesem Fall zu spät.

Wikipedia Und ein paar andere Stapelüberlauffragen haben gute Diskussionen über "doppelte Überprüfung", also würde ich empfehlen, darüber nachzudenken. Ich würde es auch nicht verwenden, es nicht zu verwenden, es sei denn, das Profiling zeigt einen echten Bedarf an Leistung in diesem bestimmten Code.

Es gibt eine sicherere und lesbarere Idiom für die faule Initialisierung von Java -Singletons:

class Singleton {

  private static class Holder {
    static final Singleton instance = create();
  }

  static Singleton getInstance() { 
    return Holder.instance; 
  }

  private Singleton create() {
    ⋮
  }

  private Singleton() { }

}

Wenn Sie das ausführlichere Doppelsperrmuster verwenden, müssen Sie das Feld deklarieren volatile, wie andere bereits bemerkt haben.

Nein, es muss nicht volatil sein. Sehen Wie löst ich die Erklärung "Double Checked Locking ist gebrochen" in Java?

Frühere Versuche sind fehlgeschlagen, da Java Sie betrügen und Ihnen eine falsche Ansicht des Objekts geben kann, wenn Sie Java betrügen und flüchtig/synchronisiert werden können. Allerdings das Neue final Die Semantik löst das Problem. Wenn Sie eine Objektreferenz (nach gewöhnlicher Lektüre) erhalten, können Sie ihre sicher lesen final Felder.

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