it-swarm-eu.dev

Wie lässt sich ein Singleton-Muster in Java effizient implementieren?

Wie lässt sich ein Singleton-Muster in Java effizient implementieren?

752

Verwenden Sie eine Aufzählung:

public enum Foo {
    INSTANCE;
}

Joshua Bloch erklärte diesen Ansatz in seinem Effective Java Reloaded Talk auf Google I/O 2008: Link zum Video . Siehe auch Folien 30-32 seiner Präsentation ( effect_Java_reloaded.pdf ):

Der richtige Weg, um ein serialisierbares Singleton zu implementieren

public enum Elvis {
    INSTANCE;
    private final String[] favoriteSongs =
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
}

Edit: Ein Online-Teil von "Effective Java" sagt: 

"Dieser Ansatz entspricht funktionell dem Ansatz des Public-Field-Bereichs, abgesehen davon, dass er kürzer ist, die Serialisierungsmechanismen kostenlos zur Verfügung stellt und eine unübertroffene Garantie gegen mehrfache Instantiierung bietet, selbst angesichts komplexer Serialisierungs- oder Reflexionsattacken Noch nicht weit verbreitet, ist ein Einzelelement-Enummentyp der beste Weg, ein Singleton zu implementieren. "

743
Stephen Denne

Je nach Verwendung gibt es mehrere "richtige" Antworten.

Seit Java5 ist es am besten, eine Enumeration zu verwenden:

public enum Foo {
   INSTANCE;
}

Vor Java5 ist der einfachste Fall:

public final class Foo {

    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }

    public Object clone() throws CloneNotSupportedException{
        throw new CloneNotSupportedException("Cannot clone instance of this class");
    }
}

Gehen wir den Code durch. Zuerst möchten Sie, dass die Klasse endgültig ist. In diesem Fall habe ich das Schlüsselwort final verwendet, um die Benutzer darüber zu informieren, dass es endgültig ist. Dann müssen Sie den Konstruktor als privat definieren, damit Benutzer keine eigenen Foo erstellen können. Durch das Auslösen einer Ausnahme vom Konstruktor wird verhindert, dass Benutzer Reflection verwenden, um ein zweites Foo zu erstellen. Dann erstellen Sie ein private static final Foo-Feld für die einzige Instanz und eine public static Foo getInstance()-Methode, um diese zurückzugeben. Die Java-Spezifikation stellt sicher, dass der Konstruktor nur aufgerufen wird, wenn die Klasse zum ersten Mal verwendet wird.

Wenn Sie über ein sehr umfangreiches Objekt oder einen umfangreichen Konstruktionscode verfügen UND auch über andere zugängliche statische Methoden oder Felder verfügen, die möglicherweise verwendet werden, bevor eine Instanz benötigt wird, müssen Sie nur dann die verzögerte Initialisierung verwenden.

Sie können einen private static class verwenden, um die Instanz zu laden. Der Code würde dann so aussehen:

public final class Foo {

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }
}

Da die Zeile private static final Foo INSTANCE = new Foo(); nur ausgeführt wird, wenn die Klasse FooLoader tatsächlich verwendet wird, kümmert sich diese um die Lazy-Instantiierung und ist garantiert Thread-sicher.

Wenn Sie Ihr Objekt auch serialisieren möchten, müssen Sie sicherstellen, dass durch die Deserialisierung keine Kopie erstellt wird.

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

Die Methode readResolve() stellt sicher, dass die einzige Instanz zurückgegeben wird, selbst wenn das Objekt in einer vorherigen Ausführung Ihres Programms serialisiert wurde.

224
Roel Spilker

Disclaimer: Ich habe gerade alle tollen Antworten zusammengefasst und in meine Worte geschrieben.


Bei der Implementierung von Singleton haben wir zwei Möglichkeiten
1. Faules Laden
2. Frühes Laden

Lazy Loading fügt etwas Overhead hinzu (um ehrlich zu sein). Verwenden Sie es daher nur, wenn Sie ein sehr umfangreiches Objekt oder einen umfangreichen Konstruktionscode haben UND auch über andere zugängliche statische Methoden oder Felder verfügen, die verwendet werden könnten, bevor eine Instanz benötigt wird Sie müssen eine langsame Initialisierung verwenden. Andernfalls ist die Wahl des frühen Ladens eine gute Wahl.

Der einfachste Weg zur Implementierung von Singleton ist 

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }
}

Alles ist gut bis auf das früh geladene Singleton. Versuchen wir mal faul geladenes Singleton

class Foo {

    // Our now_null_but_going_to_be sole hero 
    private static Foo INSTANCE = null;

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT  
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        // Creating only  when required.
        if (INSTANCE == null) {
            INSTANCE = new Foo();
        }
        return INSTANCE;
    }
}

So weit so gut, aber unser Held wird nicht überleben, wenn er allein mit mehreren bösen Fäden kämpft, die viele Beispiele unseres Helden wollen ... _. Also können wir ihn vor bösem Multi-Threading schützen

class Foo {

    private static Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        // No more tension of threads
        synchronized (Foo.class) {
            if (INSTANCE == null) {
                INSTANCE = new Foo();
            }
        }
        return INSTANCE;
    }
}

aber es reicht nicht aus, den Helden zu beschützen, Wirklich !!! Dies ist das Beste, was wir tun können/sollten, um unserem Helden zu helfen 

class Foo {

    // Pay attention to volatile
    private static volatile Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        if (INSTANCE == null) { // Check 1
            synchronized (Foo.class) {
                if (INSTANCE == null) { // Check 2
                    INSTANCE = new Foo();
                }
            }
        }
        return INSTANCE;
    }
}

Dies wird als "Double Checked Locking Idiom" bezeichnet. Es ist leicht, die flüchtige Aussage zu vergessen und schwer zu verstehen, warum dies notwendig ist.
Für Details: http://www.cs.umd.edu/~pugh/Java/memoryModel/DoubleCheckedLocking.html

Jetzt sind wir uns sicher, was den bösen Faden angeht, aber was ist mit der grausamen Serialisierung? Wir müssen sicherstellen, dass auch bei der De-Serialisierung kein neues Objekt erstellt wird

class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static volatile Foo INSTANCE = null;

    // Rest of the things are same as above

    // No more fear of serialization
    @SuppressWarnings("unused")
    private Object readResolve() {
        return INSTANCE;
    }
}

Die Methode readResolve() sorgt dafür, dass die einzige Instanz zurückgegeben wird, selbst wenn das Objekt in einer vorherigen Ausführung unseres Programms serialisiert wurde.

Schließlich haben wir ausreichend Schutz gegen Threads und Serialisierung hinzugefügt, aber unser Code sieht sperrig und hässlich aus. Lasst uns unseren Helden überholen

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    // Wrapped in a inner static class so that loaded only when required
    private static class FooLoader {

        // And no more fear of threads
        private static final Foo INSTANCE = new Foo();
    }

    // TODO add private shouting construcor

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    // Damn you serialization
    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

Ja das ist unser ganzer Held :)
Da die Zeile private static final Foo INSTANCE = new Foo(); nur ausgeführt wird, wenn die Klasse FooLoader tatsächlich verwendet wird, kümmert sich dies um die Lazy-Instantiierung. 

und ist es garantiert Thread-sicher.

Und wir sind so weit gekommen, hier ist der beste Weg, alles zu erreichen, was wir getan haben, der beste Weg 

 public enum Foo {
       INSTANCE;
   }

Was intern behandelt wird 

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();
}

Das ist es keine Angst mehr vor Serialisierung, Threads und hässlichem Code. Auch ENUMS Singleton werden faul initialisiert

Dieser Ansatz entspricht funktional dem Ansatz des öffentlichen Feldes abgesehen davon, dass es übersichtlicher ist, stellt die Serialisierungsmaschine bereit kostenlos und bietet eine mehrfache Garantie Instantiierung, auch angesichts anspruchsvoller Serialisierung oder Reflexionsangriffe. Während dieser Ansatz noch weit verbreitet ist, wird Ein Einzeltyp ist der beste Weg, um einen Singleton zu implementieren.

-Joshua Bloch in "Effektives Java" 

Jetzt haben Sie vielleicht erkannt, warum ENUMS als beste Methode zur Implementierung von Singleton angesehen wird, und bedanken uns für Ihre Geduld
Es wurde in meinem blog aktualisiert. 

127
xyz

Die von Stu Thompson veröffentlichte Lösung ist in Java5.0 und höher gültig. Ich würde es aber lieber nicht verwenden, weil ich denke, dass es fehleranfällig ist.

Es ist leicht, die flüchtige Aussage zu vergessen und schwer zu verstehen, warum dies notwendig ist. Ohne den flüchtigen Code wäre dieser Code aufgrund des doppelt geprüften Antipatterns für die Verriegelung nicht mehr threadsicher. Weitere Informationen hierzu finden Sie in Abschnitt 16.2.4 von Java Concurrency in Practice . Kurz gesagt: Dieses Muster (vor Java5.0 oder ohne die volatile-Anweisung) könnte einen Verweis auf das Bar-Objekt zurückgeben, das sich (noch) in einem falschen Zustand befindet.

Dieses Muster wurde zur Leistungsoptimierung erfunden. Aber das ist wirklich kein Problem mehr. Der folgende langsame Initialisierungscode ist schnell und, was noch wichtiger ist, einfacher zu lesen.

class Bar {
    private static class BarHolder {
        public static Bar bar = new Bar();
    }

    public static Bar getBar() {
        return BarHolder.bar;
    }
}
121
Benno Richters

Threadsicher in Java 5+:

class Foo {
    private static volatile Bar bar = null;
    public static Bar getBar() {
        if (bar == null) {
            synchronized(Foo.class) {
                if (bar == null)
                    bar = new Bar(); 
            }
        }
        return bar;
    }
}

EDIT: Beachten Sie hier den Modifizierer volatile. :) Es ist wichtig, da andere Threads vom JMM (Java Memory Model) nicht garantiert werden können, um Änderungen an ihrem Wert zu sehen. Dafür sorgt die Synchronisation nicht - sie serialisiert nur den Zugriff auf diesen Codeblock.

EDIT 2 : @ Bnos Antwort beschreibt den von Bill Pugh (FindBugs) empfohlenen Ansatz und ist umstrittener. Lesen und lesen Sie auch seine Antwort.

94
Stu Thompson

Vergessen Sie faule Initialisierung , es ist zu problematisch. Dies ist die einfachste Lösung:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}
90
Jonathan

Stellen Sie sicher, dass Sie es wirklich brauchen. Machen Sie ein Google für "Singleton Anti-Pattern", um einige Argumente dagegen zu sehen. Vermutlich ist nichts an sich falsch, aber es ist nur ein Mechanismus, um globale Ressourcen/Daten verfügbar zu machen. Stellen Sie also sicher, dass dies der beste Weg ist. Insbesondere habe ich die Abhängigkeitsinjektion als sinnvoller erachtet, insbesondere wenn Sie auch Komponententests verwenden, da Sie mit gedämpften Ressourcen zu Testzwecken verwenden können.

47
Neil Burroughs

Vergessen Sie nicht, dass Singleton nur ein Singleton für den Classloader ist, der es geladen hat. Wenn Sie mehrere Loader (Container) verwenden, hat jeder COULD eine eigene Version des Singleton.

26
Javamann

Ich bin durch einige der Antworten verwirrt, die DI als Alternative zur Verwendung von Singletons vorschlagen. Dies sind nicht miteinander verwandte Begriffe. Sie können DI verwenden, um entweder Singleton- oder Nicht-Singleton-Instanzen (z. B. pro Thread) einzufügen. Zumindest ist dies der Fall, wenn Sie Spring 2.x verwenden. Ich kann nicht für andere DI-Frameworks sprechen.

Also meine Antwort auf das OP wäre (in allen außer dem trivialsten Beispielcode):

  1. Verwenden Sie dann ein DI-Framework wie Spring
  2. Machen Sie es zu Ihrer DI-Konfiguration, unabhängig davon, ob es sich bei Ihren Abhängigkeiten um Singletons, Anforderungsbereiche, Sitzungsbereiche oder was auch immer handelt.

Dieser Ansatz bietet Ihnen eine Nice-entkoppelte (und daher flexible und überprüfbare) Architektur, bei der es sich bei der Verwendung eines Singleton um ein leicht umkehrbares Implementierungsdetail handelt (vorausgesetzt, die von Ihnen verwendeten Singletons sind natürlich threadsicher).

21
Andrew Swan

Überlegen Sie wirklich, warum Sie ein Singleton benötigen, bevor Sie es schreiben. Es gibt eine quasi-religiöse Debatte über deren Verwendung, über die Sie leicht stolpern können, wenn Sie Singletons in Java durchsuchen.

Persönlich versuche ich, Singletons aus vielen Gründen so oft wie möglich zu vermeiden, von denen die meisten durch Googeln von Singletons gefunden werden können. Ich habe das Gefühl, dass Singletons oft missbraucht werden, weil sie von jedem leicht verstanden werden. Sie werden als Mechanismus verwendet, um "globale" Daten in ein OO - Design zu bringen, und sie werden verwendet, weil Objekte leicht umgangen werden können Lifecycle-Management (oder wirklich darüber nachdenken, wie Sie A aus B heraus tun können). Sehen Sie sich Dinge wie Inversion of Control (IoC) oder Dependency Injection (DI) für einen schönen Mittelweg an.

Wenn Sie wirklich einen benötigen, hat Wikipedia ein gutes Beispiel für die korrekte Implementierung eines Singleton.

20
Aidos

Es folgen 3 verschiedene Ansätze

1) Aufzählung

/**
* Singleton pattern example using Java Enumj
*/
public enum EasySingleton{
    INSTANCE;
}

2) Doppelte Überprüfung von Sperren/Lazy Loading

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private static volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public static DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

3) Statische Fabrikmethode

/**
* Singleton pattern example with static factory method
*/

public class Singleton{
    //initailzed during class loading
    private static final Singleton INSTANCE = new Singleton();

    //to prevent creating another instance of Singleton
    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}
16
Abhijit Gaikwad

Ich verwende das Spring Framework, um meine Singletons zu verwalten. Es erzwingt nicht die "Singleton-Ness" der Klasse (was Sie eigentlich nicht tun können, wenn mehrere Klassenlader beteiligt sind), bietet aber eine wirklich einfache Möglichkeit, verschiedene Factorys zu erstellen und zu konfigurieren, um verschiedene Objekttypen zu erstellen.

13
Matt

Version 1:

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

Lazy Loading, Thread-sicher mit Blockierung, geringe Leistung wegen synchronized.

Version 2:

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

Faules Laden, fadensicher, nicht blockierend, hohe Leistung.

11
coderz

Wikipedia hat einige Beispiele von Singletons, auch in Java. Die Java 5-Implementierung sieht ziemlich vollständig aus und ist Thread-sicher (doppelt gesicherte Sperre angewendet).

10
macbirdie

Wenn Sie kein Lazy Loading benötigen, versuchen Sie es einfach

public class Singleton {
    private final static Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() { return Singleton.INSTANCE; }

    protected Object clone() {
        throw new CloneNotSupportedException();
    }
}

Wenn Sie ein langsames Laden wünschen und möchten, dass Ihr Singleton Thread-sicher ist, probieren Sie das doppelte Prüfmuster aus 

public class Singleton {
        private static Singleton instance = null;

        private Singleton() {}

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

        protected Object clone() {
            throw new CloneNotSupportedException();
        }
}

Da das Double-Check-Muster nicht garantiert funktioniert (aufgrund von Problemen mit Compilern weiß ich darüber nicht mehr.), Könnten Sie auch versuchen, die gesamte getInstance-Methode zu synchronisieren oder eine Registrierung für alle Ihre Singletons zu erstellen. 

10
Aleksi Yrttiaho

Könnte ein wenig zu spät ins Spiel kommen, aber die Implementierung eines Singletons bringt eine Menge Nuancen mit sich. Das Haltemuster kann in vielen Situationen nicht verwendet werden. Und IMO, wenn Sie eine flüchtige Variable verwenden - Sie sollten auch eine lokale Variable verwenden. Beginnen wir am Anfang und wiederholen Sie das Problem. Du wirst sehen, was ich meine.


Der erste Versuch könnte ungefähr so ​​aussehen:

public class MySingleton {

     private static MySingleton INSTANCE;

     public static MySingleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new MySingleton();
        }

        return INSTANCE;
    }
    ...
}

Hier haben wir die MySingleton-Klasse mit einem privaten statischen Member namens INSTANCE und einer öffentlichen statischen Methode namens getInstance (). Beim ersten Aufruf von getInstance () ist das INSTANCE-Member null. Der Flow fällt dann in die Erstellungsbedingung und erstellt eine neue Instanz der MySingleton-Klasse. Nachfolgende Aufrufe von getInstance () stellen fest, dass die Variable INSTANCE bereits festgelegt ist, und erstellen daher keine weitere MySingleton-Instanz. Dadurch wird sichergestellt, dass nur eine Instanz von MySingleton für alle Aufrufer von getInstance () freigegeben ist.

Diese Implementierung hat jedoch ein Problem. Multithread-Anwendungen haben eine Race-Bedingung bei der Erstellung der einzelnen Instanz. Wenn mehrere Ausführungsthreads gleichzeitig auf die Methode getInstance () treffen, wird das INSTANCE-Member jeweils als null angezeigt. Dies führt dazu, dass jeder Thread eine neue MySingleton-Instanz erstellt und anschließend das INSTANCE-Mitglied festlegt.


private static MySingleton INSTANCE;

public static synchronized MySingleton getInstance() {
    if (INSTANCE == null) {
        INSTANCE = new MySingleton();
    }

    return INSTANCE;
}

Hier haben wir das synchronized-Schlüsselwort in der Methodensignatur verwendet, um die getInstance () -Methode zu synchronisieren. Dies wird sicherlich unseren Rennzustand verbessern. Die Threads werden nun blockiert und geben nacheinander die Methode ein. Es entsteht aber auch ein Leistungsproblem. Diese Implementierung synchronisiert nicht nur die Erstellung der einzelnen Instanz, sondern auch alle Aufrufe von getInstance (), einschließlich Reads. Lesevorgänge müssen nicht synchronisiert werden, da sie einfach den Wert von INSTANCE zurückgeben. Da Lesevorgänge den Großteil unserer Aufrufe ausmachen (denken Sie daran, dass die Instantiierung nur beim ersten Aufruf erfolgt), wird die Leistung durch die Synchronisierung der gesamten Methode unnötig beeinträchtigt.


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronize(MySingleton.class) {
            INSTANCE = new MySingleton();
        }
    }

    return INSTANCE;
}

Hier haben wir die Synchronisation von der Methodensignatur in einen synchronisierten Block verschoben, der die Erstellung der MySingleton-Instanz abschließt. Aber löst das unser Problem? Wir blockieren keine Lesevorgänge mehr, sind aber auch einen Schritt zurückgegangen. Mehrere Threads treffen die getInstance () -Methode zur gleichen Zeit oder ungefähr zur gleichen Zeit und sehen alle das INSTANCE-Member als null. Sie schlagen dann den synchronisierten Block an, wo man die Sperre erhält und die Instanz erstellt. Wenn dieser Thread den Block verlässt, kämpfen die anderen Threads um die Sperre, und jeder Thread fällt einzeln durch den Block und erstellt eine neue Instanz unserer Klasse. Wir sind also wieder da, wo wir angefangen haben.


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

Hier stellen wir einen weiteren Scheck von INSIDE des Blocks aus. Wenn das INSTANCE-Mitglied bereits festgelegt wurde, überspringen wir die Initialisierung. Dies wird als doppelt überprüfte Verriegelung bezeichnet.

Dies löst unser Problem der mehrfachen Instanziierung. Unsere Lösung hat abermals eine weitere Herausforderung dargestellt. Andere Threads sehen möglicherweise nicht, dass das INSTANCE-Mitglied aktualisiert wurde. Dies liegt daran, wie Java die Speicheroperationen optimiert. Threads kopieren die ursprünglichen Werte von Variablen aus dem Hauptspeicher in den Cache der CPU. Änderungen an den Werten werden dann in diesen Cache geschrieben und daraus gelesen. Dies ist eine Funktion von Java zur Leistungsoptimierung. Dies stellt jedoch ein Problem für unsere Singleton-Implementierung dar. Ein zweiter Thread, der von einer anderen CPU oder einem anderen Core mit einem anderen Cache verarbeitet wird, wird nicht erkannt Dies bewirkt, dass der zweite Thread das INSTANCE-Member als null ansieht und das Erstellen einer neuen Instanz unseres Singletons erzwingt.


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

Wir lösen dieses Problem, indem wir das Schlüsselwort volatile in der Deklaration des INSTANCE-Mitglieds verwenden. Dadurch wird der Compiler angewiesen, immer aus dem Hauptspeicher und nicht aus dem CPU-Cache zu lesen und in diesen zu schreiben.

Diese einfache Änderung ist jedoch mit Kosten verbunden. Da wir den CPU-Cache umgehen, werden wir jedes Mal, wenn wir das flüchtige INSTANCE-Mitglied bearbeiten, einen Leistungseinbruch hinnehmen - was wir viermal tun. Wir überprüfen die Existenz (1 und 2), setzen den Wert (3) und geben dann den Wert (4) zurück. Man könnte argumentieren, dass dieser Pfad der Randfall ist, da wir die Instanz nur beim ersten Aufruf der Methode erstellen. Vielleicht ist ein Leistungstreffer bei der Erstellung erträglich. Aber selbst unser Hauptanwendungsfall, liest, wird das flüchtige Mitglied zweimal bearbeiten. Einmal, um die Existenz zu überprüfen und erneut, um ihren Wert zurückzugeben.


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    MySingleton result = INSTANCE;
    if (result == null) {
        synchronized(MySingleton.class) {
            result = INSTANCE;
            if (result == null) {
                INSTANCE = result = createInstance();
            }
        }
    }

    return result;
}

Da der Leistungseinbruch darauf zurückzuführen ist, dass das flüchtige Element direkt bearbeitet wird, setzen wir eine lokale Variable auf den Wert des flüchtigen Elements und bearbeiten stattdessen die lokale Variable. Dies verringert die Häufigkeit, mit der wir mit volatilen Daten arbeiten, und führt dazu, dass wir einen Teil unserer verlorenen Leistung zurückerhalten. Beachten Sie, dass wir unsere lokale Variable erneut setzen müssen, wenn wir den synchronisierten Block betreten. Auf diese Weise wird sichergestellt, dass alle Änderungen, die während des Wartens auf das Schloss vorgenommen wurden, auf dem neuesten Stand sind.

Ich habe kürzlich einen Artikel darüber geschrieben. Deconstructing The Singleton . Dort finden Sie weitere Informationen zu diesen Beispielen und ein Beispiel für das "Inhaber" -Muster. Es gibt auch ein reales Beispiel, das den doppelt überprüften volatilen Ansatz zeigt. Hoffe das hilft.

8
Michael Andrews

Ich würde sagen, Enum Singleton 

Singleton, das enum in Java verwendet, ist im Allgemeinen ein Weg, um enum singleton zu deklarieren. Enum Singleton kann Instanzvariable und Instanzmethode enthalten. Der Einfachheit halber sollten Sie auch beachten, dass Sie, wenn Sie eine Instanzmethode verwenden, die Threadsicherheit dieser Methode gewährleisten müssen, wenn sie den Status des Objekts beeinflusst.

Die Verwendung einer Enumeration ist sehr einfach zu implementieren und hat keine Nachteile hinsichtlich serialisierbarer Objekte, die auf andere Weise umgangen werden müssen.

/**
* Singleton pattern example using Java Enum
*/
public enum Singleton {
        INSTANCE;
        public void execute (String arg) {
                //perform operation here
        }
}

Sie können darauf mit Singleton.INSTANCE zugreifen, viel einfacher als mit der getInstance()-Methode für Singleton.

1.12 Serialisierung von Enumenkonstanten

Enumerationskonstanten werden anders als gewöhnliche serialisierbare oder externalisierbare Objekte serialisiert. Die serialisierte Form einer Enumenkonstante besteht nur aus ihrem Namen. Feldwerte der Konstante sind im Formular nicht vorhanden. Um eine Enumenkonstante zu serialisieren, schreibt ObjectOutputStream den Wert, der von der name-Methode der enum-Konstante zurückgegeben wird. Um eine Enumenkonstante zu deserialisieren, liest ObjectInputStream den Konstantennamen aus dem Stream. Die deserialisierte Konstante wird dann durch Aufrufen der Java.lang.Enum.valueOf-Methode erhalten, indem der Aufzählungstyp der Konstante zusammen mit dem erhaltenen Konstantennamen als Argumente übergeben wird. Wie andere serialisierbare oder externalisierbare Objekte können Enumerationskonstanten als Ziele für Rückverweise dienen, die später im Serialisierungsstrom erscheinen.

Der Prozess, mit dem die Enumenkonstanten serialisiert werden, kann nicht angepasst werden: Klassenspezifische writeObject-, readObject-, readObjectNoData-, writeReplace- und readResolve-Methoden, die durch Aufzählungstypen definiert werden, werden während der Serialisierung und Deserialisierung ignoriert. Ebenso werden alle serialPersistentFields- oder serialVersionUID-Felddeklarationen ignoriert - alle Enummentypen haben eine feste serialVersionUID von 0L. Das Dokumentieren von serialisierbaren Feldern und Daten für Aufzählungstypen ist nicht erforderlich, da sich die Art der gesendeten Daten nicht ändert.

Zitiert aus Oracle-Dokumenten

Ein weiteres Problem bei herkömmlichen Singletons besteht darin, dass sie nach dem Implementieren der Serializable-Schnittstelle nicht mehr Singleton bleiben, da die readObject()-Methode in Java immer eine neue Instanz wie den Konstruktor zurückgibt. Dies kann vermieden werden, indem Sie readResolve() verwenden und eine neu erstellte Instanz verwerfen, indem Sie sie wie unten beschrieben durch Singleton ersetzen 

 // readResolve to prevent another instance of Singleton
 private Object readResolve(){
     return INSTANCE;
 }

Dies kann noch komplexer werden, wenn Ihre Singleton-Klasse den Status beibehält, da sie vorübergehend sein müssen. Mit Enum Singleton wird die Serialisierung jedoch von JVM garantiert.


Gute Lektüre

  1. Singleton Pattern
  2. Enums, Singletons und Deserialisierung
  3. Überprüftes Sperren und das Singleton-Muster
8
NullPoiиteя
There are 4 ways to create a singleton in Java.

1- eager initialization singleton

    public class Test{
        private static final Test test = new Test();
        private Test(){}
        public static Test getTest(){
            return test;
        }
    }

2- lazy initialization singleton (thread safe)

    public class Test {
         private static volatile Test test;
         private Test(){}
         public static Test getTest() {
            if(test == null) {
                synchronized(Test.class) {
                    if(test == null){test = new Test();
                }
            }
         }

        return test;
    }


3- Bill Pugh Singleton with Holder Pattern (Preferably the best one)

    public class Test {

        private Test(){}

        private static class TestHolder{
            private static final Test test = new Test();
        }

        public static Test getInstance(){
            return TestHolder.test;
        }
    }

4- enum singleton
      public enum MySingleton {
        INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}
7
Dheeraj Sachan

So implementieren Sie eine einfache singleton:

public class Singleton {
    // It must be static and final to prevent later modification
    private static final Singleton INSTANCE = new Singleton();
    /** The constructor must be private to prevent external instantiation */ 
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

So erstellen Sie Ihre singleton richtig faul:

public class Singleton {
    // The constructor must be private to prevent external instantiation   
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    /** 
     * The static inner class responsible for creating your instance only on demand,
     * because the static fields of a class are only initialized when the class
     * is explicitly called and a class initialization is synchronized such that only 
     * one thread can perform it, this rule is also applicable to inner static class
     * So here INSTANCE will be created only when SingletonHolder.INSTANCE 
     * will be called
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}
4
Nicolas Filotto

Sie benötigen double-testing idiom, wenn Sie die Instanzvariable einer Klasse träge laden müssen. Wenn Sie eine statische Variable oder ein Singleton träge laden müssen, benötigen Sie Initilization on demand holder idiom. 

Wenn der Singleton seriliazble sein muss, müssen alle anderen Felder vorübergehend sein und die readResolve () -Methode muss implementiert werden, um die Invariante des Singleton-Objekts aufrechtzuerhalten. Andernfalls wird bei jeder Deserialisierung des Objekts eine neue Instanz des Objekts erstellt. Was readResolve () tut, ist das neue Objekt, das von readObject () gelesen wird, zu ersetzen, wodurch das neue Objekt zur Sammlung von Garbage gezwungen wurde, da keine Variable darauf verweist.

public static final INSTANCE == ....
private Object readResolve() {
  return INSTANCE; // original singleton instance.
} 
3
Onur

Verschiedene Möglichkeiten, Singleton-Objekte zu erstellen:

  1. Laut Joshua Bloch wäre Enum das Beste.

  2. sie können auch die doppelte Checksperre verwenden.

  3. Sogar die innere statische Klasse kann verwendet werden.

3

Enum Singleton

Die einfachste Methode zum Implementieren eines Singleton-Threads ist die Verwendung eines Enums

public enum SingletonEnum {
  INSTANCE;
  public void doSomething(){
    System.out.println("This is a singleton");
  }
}

Dieser Code funktioniert seit der Einführung von Enum in Java 1.5

Doppelte überprüfte Verriegelung

Wenn Sie einen "klassischen" Singleton codieren möchten, der in einer Multithread-Umgebung (ab Java 1.5) funktioniert, sollten Sie diesen verwenden.

public class Singleton {

  private static volatile Singleton instance = null;

  private Singleton() {
  }

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

Dies ist vor 1.5 nicht threadsicher, da die Implementierung des volatilen Schlüsselworts unterschiedlich war.

Frühes Laden von Singleton (funktioniert auch vor Java 1.5)

Diese Implementierung instanziiert den Singleton, wenn die Klasse geladen wird, und bietet Thread-Sicherheit.

public class Singleton {

  private static final Singleton instance = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    return instance;
  }

  public void doSomething(){
    System.out.println("This is a singleton");
  }

}
3
Dan Moldovan

Verwenden Sie für JSE 5.0 und höher den Enum-Ansatz, andernfalls den statischen Singleton-Holder-Ansatz ((ein von Bill Pugh beschriebener Lazy-Loading-Ansatz). Die letztere Lösung ist auch Thread-sicher, ohne dass spezielle Sprachkonstrukte (d. H. Flüchtig oder synchronisiert) erforderlich sind.

2
raoadnan

Ein weiteres Argument, das häufig gegen Singletons verwendet wird, sind deren Testbarkeitsprobleme. Singletons sind zu Testzwecken nicht leicht zu verspotten. Wenn sich dies als Problem herausstellt, möchte ich folgende geringfügige Änderung vornehmen:

public class SingletonImpl {

    private static SingletonImpl instance;

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

    public static void setInstance(SingletonImpl impl) {
        instance = impl;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

Die hinzugefügte setInstance-Methode erlaubt das Setzen einer Mockup-Implementierung der Singleton-Klasse während des Testens:

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

Dies funktioniert auch bei frühen Initialisierungsansätzen:

public class SingletonImpl {

    private static final SingletonImpl instance = new SingletonImpl();

    private static SingletonImpl alt;

    public static void setInstance(SingletonImpl inst) {
        alt = inst;
    }

    public static SingletonImpl getInstance() {
        if (alt != null) {
            return alt;
        }
        return instance;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

Dies hat den Nachteil, dass diese Funktionalität auch für die normale Anwendung verfügbar gemacht wird. Andere Entwickler, die an diesem Code arbeiten, könnten versucht sein, die ´setInstance'-Methode zu verwenden, um eine bestimmte Funktion zu ändern und somit das gesamte Anwendungsverhalten zu ändern. Daher sollte diese Methode mindestens eine gute Warnung in ihrem Javadoc enthalten.

Für die Möglichkeit eines Mockup-Tests (falls erforderlich) kann dieses Code-Engagement jedoch ein akzeptabler Preis sein.

2
user3792852

einfachste Singleton-Klasse

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}
1
rohan kamat

Ich denke nach Java 1.5 immer noch, dass enum die beste verfügbare Singleton-Implementierung ist, da es auch in Multi-Threaded-Umgebungen gewährleistet, dass nur eine Instanz erstellt wird.

public enum Singleton{ INSTANCE; }

und du bist fertig !!!

0
shikjohari

Schauen Sie sich diesen Beitrag an.

Beispiele für GoF-Entwurfsmuster in Java-Kernbibliotheken

Aus der "Singleton" -Sektion der besten Antwort,

Singleton (erkennbar durch kreative Methoden, die jedes Mal dieselbe Instanz zurückgeben)

  • Java.lang.Runtime # getRuntime ()
  • Java.awt.Desktop # getDesktop ()
  • Java.lang.System # getSecurityManager ()

Sie können das Beispiel von Singleton auch von den Java-Klassen selbst lernen.

0
phi

Das beste Singleton-Pattern, das ich je gesehen habe, verwendet die Supplier-Schnittstelle.

  • Es ist generisch und wiederverwendbar
  • Es unterstützt faule Initialisierung
  • Es wird nur synchronisiert, bis es initialisiert wurde. Dann wird der blockierende Lieferant durch einen nicht blockierenden Lieferanten ersetzt.

Siehe unten:

public class Singleton<T> implements Supplier<T> {

    private boolean initialized;
    private Supplier<T> singletonSupplier;

    public Singleton(T singletonValue) {
        this.singletonSupplier = () -> singletonValue;
    }

    public Singleton(Supplier<T> supplier) {
        this.singletonSupplier = () -> {
            // The initial supplier is temporary; it will be replaced after initialization
            synchronized (supplier) {
                if (!initialized) {
                    T singletonValue = supplier.get();
                    // Now that the singleton value has been initialized,
                    // replace the blocking supplier with a non-blocking supplier
                    singletonSupplier = () -> singletonValue;
                    initialized = true;
                }
                return singletonSupplier.get();
            }
        };
    }

    @Override
    public T get() {
        return singletonSupplier.get();
    }
}
0
user1024314