Was ist ein effizienter Weg, um eine Singletonmuster in Java zu implementieren? [geschlossen]

StackOverflow https://stackoverflow.com/questions/70689

  •  09-06-2019
  •  | 
  •  

Frage

Was ist ein effizienter Weg, um eine Singletonmuster in Java?

zu implementieren
War es hilfreich?

Lösung

Verwenden Sie eine ENUM:

public enum Foo {
    INSTANCE;
}

Joshua Bloch erklärt, diesen Ansatz in seiner Effective Java Reloaded Vortrag auf Google I / O 2008: Link zu Video . Siehe auch Folien 30-32 seiner Präsentation ( effective_java_reloaded.pdf ):

  

Der richtige Weg eine serialisierbare Singleton

zur Umsetzung
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 ist funktional äquivalent zu dem öffentlichen Bereich Ansatz, mit der Ausnahme, dass es prägnanter ist, liefert die Serialisierung Maschinen kostenlos und bietet eine ironclad Garantie gegen Mehrfachinstanzierung, auch angesichts der anspruchsvollen Serialisierung oder Reflection Attacks. Während dieser Ansatz hat sich bisher weithin angenommen werden, ein Einzelelement-enum-Typ ist der beste Weg, um eine Singleton zu implementieren. "

Andere Tipps

Je nach Nutzung, gibt es mehr „richtige“ Antworten.

Da der beste Weg java5 es zu tun ist, eine ENUM zu verwenden:

public enum Foo {
   INSTANCE;
}

Pre java5, der einfachste Fall ist:

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");
    }
}

Lassen Sie uns über den Code gehen. Erstens wollen Sie die Klasse endgültig zu sein. In diesem Fall habe ich die final Schlüsselwort verwendet die Benutzer wissen, es ist endgültig. Dann müssen Sie den Konstruktor privat Benutzer daran zu hindern, ihre eigenen Foo zu erstellen. eine Ausnahme von dem Konstruktor werfen verhindert, dass Benutzer Reflexion verwenden, um einen zweit Foo zu erstellen. Dann erstellen Sie ein private static final Foo Feld die einzige Instanz zu halten, und eine public static Foo getInstance() Methode es zurückzukehren. Die Java-Spezifikation stellt sicher, dass der Konstruktor nur, wenn sie aufgerufen wird, die Klasse zum ersten Mal verwendet wird.

Wenn Sie ein sehr großes Objekt oder schwere Konstruktion Code und haben auch andere zugänglich statische Methoden oder Felder, die verwendet werden könnten, bevor eine Instanz benötigt wird, dann und nur dann müssen Sie faul Initialisierung verwenden.

Sie können eine private static class verwenden, um die Instanz zu laden. Der Code würde dann wie folgt 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 Leitung private static final Foo INSTANCE = new Foo(); nur dann ausgeführt wird, wenn die Klasse FooLoader tatsächlich verwendet wird, nimmt diese Sorge für die verzögerte Instanziierung, und es ist garantiert sicher fädeln.

Wenn Sie wollen auch in der Lage sein, Ihr Objekt serialisiert werden Sie sicher vornehmen müssen, dass die Deserialisierung wird keine Kopie erstellen.

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;
    }
}

Das Verfahren readResolve() wird die einzige Instanz sicher zurückgegeben werden, auch wenn sich das Objekt in einem vorherigen Lauf des Programms serialisiert wurde.

Hinweis: Ich habe gerade alle der ehrfürchtigen Antworten zusammengefasst und schrieb es in meinen Worten

.

Während Singleton implementieren wir haben 2 Möglichkeiten
1. Lazy Loading
2. Frühbelastung

Lazy Loading fügt Bit-Overhead (viel um ehrlich zu sein), so verwenden sie nur, wenn Sie ein sehr großes Objekt oder schwere Konstruktion Code haben und auch andere zugänglich statische Methoden oder Felder, die vor einer Instanz verwendet werden könnten, erforderlich ist, dann und nur dann brauchen Sie faul initialization.Otherwise Wahl Frühbelastung zu verwenden ist eine gute Wahl.

Die meisten einfachen Weg Singleton der Implementierung 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, mit Ausnahme des frühen loaded Singleton. Versuchen wir faul geladen 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, während allein mit mehreren bösen Fäden kämpfen, die viele viele Instanz unseres Helden wollen. So lässt sie vor bösen Multithreading 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 ist nicht genug, um Helden zu schützen heraus, wirklich !!! Dies ist die beste können wir / tun soll unseren Helden 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 "Doppel-Checked Locking-Idiom" genannt. Es ist einfach, die flüchtige Aussage und schwer zu verstehen, zu vergessen, warum es notwendig ist.
Ausführliche Informationen: http://www.cs.umd.edu/~ pugh / java / memoryModel / DoubleCheckedLocking.html

Jetzt sind wir sicher böse Faden, aber was über die grausame Serialisierung? Wir müssen sicherstellen, dass auch während de-serialiaztion 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;
    }
}

Das Verfahren readResolve() wird die einzige Instanz sicher zurückgegeben werden, auch wenn sich das Objekt in einem vorherigen Lauf unseres Programms serialisiert wurde.

Schließlich haben wir genug Schutz gegen Fäden und Serialisierung, aber unser Code sucht sperrig und hässlich hinzugefügt. Lets geben unser Held ein Make über

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 selber Held :)
Da die Leitung private static final Foo INSTANCE = new Foo(); nur dann ausgeführt wird, wenn die Klasse FooLoader tatsächlich verwendet wird, nimmt diese Sorge für die verzögerte Instanziierung,

und es ist garantiert sicher fädeln.

Und wir haben kamen so weit, hier ist der beste Weg, um alles erreichen wir bestmögliche Art und Weise getan wird,

 public enum Foo {
       INSTANCE;
   }

Welche wie

werden intern behandelt
public class Foo {

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

Das ist es nicht mehr Angst vor der Serialisierung, Fäden und hässlich Code. Auch ENUMS Singletons sind träge initialisiert .

  

Dieser Ansatz ist funktional äquivalent zu dem öffentlichen Bereich Ansatz,   außer dass es prägnanter ist, liefert die Serialisierung Maschinen   kostenlos und bietet eine ironclad Garantie gegen mehr   Instanziierung, auch angesichts der anspruchsvollen Serialisierung oder   Reflection Attacks. Während dieser Ansatz werden noch weithin angenommen,   ein Single-Element Aufzählungstyp ist der beste Weg, um einen Singleton zu implementieren.

-Joshua Bloch in "Effective Java"

Jetzt haben Sie vielleicht klar, warum ENUMS als beste Art und Weise betrachtet werden Singleton und vielen Dank für Ihre Geduld zu implementieren :)
Aktualisiert es auf meinem Blog .

Die Lösung von Stu Thompson geschrieben ist gültig in Java5.0 und später. Aber ich würde es vorziehen, es nicht zu benutzen, weil ich denke, dass es fehleranfällig ist.

Es ist einfach, die flüchtige Aussage und schwer zu verstehen, zu vergessen, warum es notwendig ist. Ohne die volatilen würde dieser Code nicht sicher Thread mehr aufgrund der doppelt überprüft Verriegelung Antipattern. Sehen Sie mehr über diese in Absatz 16.2.4 von Java Concurrency in Practice . Kurz gesagt: Dieses Muster (vor Java5.0 oder ohne das volatile statement) könnte einen Verweis auf das Bar-Objekt zurück, die (noch) in einem falschen Zustand ist

.

Dieses Muster wurde zur Performance-Optimierung erfunden. Aber das ist nicht wirklich ein echtes Problem mehr. Die folgende faul Initialisierungscode ist schnell und -Mehr wichtigsten ist leichter zu lesen.

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

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

Thread sicher 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;
    }
}

Bearbeiten : die Aufmerksamkeit auf den volatile Modifikator Hier bezahlen. :) Es ist wichtig, denn ohne sie sind andere Threads nicht von der JMM (Java Memory Model) garantiert Änderungen, um seinen Wert zu sehen. Die Synchronisation nicht kümmern, dass -. Serialisiert nur Zugriff auf diesen Codeblock

EDIT 2 : @Bno ‚s Antwort beschreibt die Vorgehensweise empfohlen von Bill Pugh (FindBugs) und ist besser vertretbar. Gehen Sie lesen und stimmen seine Antwort zu.

faul 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;
    }
}

Stellen Sie sicher, dass Sie es wirklich brauchen. Führen Sie eine Google für „Singletons anti-Muster“, um einige Argumente dagegen zu sehen. Es ist nichts falsch mit ihm nehme ich an, aber es ist nur ein Mechanismus für eine globale Ressource Belichtungs / Daten so sicher, dass dies der beste Weg ist. Insbesondere habe ich Dependency Injection nützlicher fanden insbesondere, wenn Sie auch Unit-Tests verwenden, weil DI können Sie verspotteten Ressourcen für Testzwecke verwenden.

Ich bin durch einige der Antworten mystifiziert, die unter Verwendung von Singletons DI als Alternative vorschlagen; dies sind nicht verwandte Konzepte. Sie können entweder DI Singletons oder nicht-Singleton (z.B. pro Thread) Instanzen zu injizieren. Zumindest ist dies der Fall, wenn Sie Frühling 2.x verwenden, kann ich nicht für anderen DI-Frameworks sprechen.

So ist meine Antwort auf die OP wäre (in alle, aber die meisten trivial Beispielcode) an:

  1. Verwenden Sie einen DI Frameworks wie Spring, dann
  2. Machen Sie es Teil Ihrer DI-Konfiguration, ob die Abhängigkeiten sind Singletons, Anfrage scoped, Sitzung scoped, oder was auch immer.

Dieser Ansatz gibt Ihnen eine schöne entkoppelt (und damit flexibel und prüfbar) Architektur, wo, ob ein Singleton verwenden eine leicht reversible Implementierungsdetail ist (vorausgesetzt, alle Singletons Sie verwenden sind THREAD, natürlich).

Wirklich darüber nachdenken, warum Sie einen Singleton benötigen, bevor sie zu schreiben. Es gibt eine quasi-religiöse Debatte über sie mit dem Sie ganz leicht stolpern kann, wenn man Singletons in Java Google.

Persönlich versuche ich Singletons zu vermeiden, so oft wie möglich aus vielen Gründen, wieder von denen die meisten durch googeln Singletons gefunden werden kann. Ich glaube, dass sehr oft Singletons missbraucht werden, weil sie von jedem sind leicht zu verstehen, sie sind für immer „global“ Daten in ein OO-Design als ein Mechanismus verwendet, und sie verwendet werden, weil es einfach Objekt-Lifecycle-Management zu umgehen (oder wirklich darüber nachzudenken, wie Sie ein von innen B tun können). Schauen Sie sich Dinge wie Inversion of Control (IoC) oder Dependency Injection (DI) für einen schönen Mittelgrund.

Wenn Sie wirklich brauchen dann hat wikipedia ein gutes Beispiel für eine ordnungsgemäße Durchführung eines Singleton.

Im Folgenden werden drei verschiedene Ansätze

1) Enum

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

2) doppelt geprüft Locking / träges Laden

/**
* 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 Factory-Methode

/**
* 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;
    }
}

Ich verwende das Spring Framework meine Singletons zu verwalten. Es wird nicht die „Singleton-ness“ der Klasse erzwingen (was man nicht wirklich sowieso tun können, wenn es mehrere Klassenlader beteiligt ist), sondern bietet eine wirklich einfache Möglichkeit, zu bauen und zu verschiedenen Fabriken konfigurieren für verschiedene Arten von Objekten zu schaffen.

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, Gewinde 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;
    }
}

Faul Beladung threadsicher mit nicht-blockierende, hohe Leistung.

Wikipedia hat einige Beispiele von Singletons, auch in Java. Die Java 5 Implementierung sieht ziemlich vollständig und ist thread-safe (doppelt überprüft Verriegelung angewendet wird).

Wenn Sie nicht faul Laden brauchen dann einfach versuchen

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 träges Laden wollen, und Sie möchten Ihre Singleton Thread-sicher sein, versuchen Sie die doppelten Kontrolle Muster

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();
        }
}

Wie die doppelte Kontrolle Muster nicht garantiert wird (aufgrund einiger Probleme mit Compiler, ich weiß nichts mehr darüber.), Können Sie auch versuchen, die ganze getInstance-Methode zu synchronisieren oder ein Register für alle schaffen Ihre Singletons.

Ich würde sagen, Enum Singleton

Singleton mit Enum in Java ist in der Regel Weg Enum Singleton zu erklären. ENUM Singleton kann Instanzvariable und Instanzmethode enthalten. Der Einfachheit halber, beachten Sie auch, dass, wenn Sie eine Instanz-Methode verwenden, als Sie Thread-Sicherheit dieses Verfahrens gewährleisten müssen, wenn überhaupt sie den Zustand des Objekts beeinflussen.

Die Verwendung eines ENUM ist sehr einfach zu implementieren und hat keine Nachteile bezüglich serialisierbare Objekte, die in den anderen Wegen 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 es durch Singleton.INSTANCE zugreifen, viel einfacher als getInstance() Methode auf Singleton aufrufen.

  

1,12 Serialisierung von Enum-Konstanten

     

Enum-Konstanten werden serialisiert anders als gewöhnliche serializable oder Externalizable Objekte. Die serialisierten Form einer ENUM-Konstante besteht ausschließlich aus ihren Namen; Feldwerte der Konstanten sind in der Form nicht vorhanden ist. Um eine Enumeration Konstante zu serialisiert, schreibt ObjectOutputStream den Wert von dem ENUM-Konstante Name-Methode zurückgegeben. Um eine Enumeration Konstante deserialisieren, ObjectInputStream liest die Konstante Name aus dem Strom; der deserialisierten Konstante wird dann durch den Aufruf des java.lang.Enum.valueOf Verfahrens erhalten, die Konstante der ENUM-Typs zusammen mit dem empfangenen konstanten Namen als Argument. Wie andere serializable oder Externalizable Objekte, Enum-Konstanten können als die Ziele der Rückverweise funktionieren anschließend im Serialisierungsstream erscheinen.

     

Der Prozess, durch den Aufzählungskonstanten serialisiert werden, kann nicht angepasst werden: jeder klassenspezifische writeObject, readObject, readObjectNoData, writeReplace und readResolve Methoden von Aufzählungstypen definiert werden während der Serialisierung und Deserialisierung ignoriert. In ähnlicher Weise werden alle serialPersistentFields oder serialVersionUID Felddeklarationen auch ignoriert - alle Enum-Typen haben eine feste serialVersionUID von 0L. Dokumentieren serializable Felder und Daten für die ENUM-Typen ist nicht notwendig, da es keine Veränderung in der Art der Daten gesendet wird.

     

von Oracle Zitat docs

Ein weiteres Problem bei herkömmlichen Singletons ist, dass, sobald Sie Serializable Schnittstelle implementieren, sie nicht mehr Singleton bleiben, weil readObject() Methode immer eine neue Instanz wie Konstruktor in Java zurück. Dies kann wie unten unter Verwendung readResolve() und Verwerfen neu erstellte Instanz durch den Austausch mit Singletons vermieden wird

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

Dies kann noch komplexer werden, wenn Ihre Singleton Klasse Zustand halten, wie Sie ihnen transiente müssen machen, aber mit in Enum Singleton, Serialisierung wird von JVM garantiert.


Good Read

  1. Singleton Pattern
  2. Aufzählungen, Singletons und Deserialisierung
  3. doppelt geprüft Verriegelung und das Singleton-Muster

Vielleicht ein wenig zu spät, um das Spiel auf diesem, aber es gibt eine Menge von Nuance um einen Singleton implementieren. Der Halter Muster kann nicht in vielen Situationen verwendet werden. Und IMO, wenn ein flüchtiges verwenden - Sie sollten auch eine lokale Variable verwenden. Lassen Sie sich am Anfang und durchläuft das Problem starten. Sie werden sehen, was ich meine.


Der erste Versuch könnte wie folgt aussehen:

public class MySingleton {

     private static MySingleton INSTANCE;

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

        return INSTANCE;
    }
    ...
}

Wir haben hier die MySingleton Klasse, die über ein eigenes statisches Element namens INSTANZ, und eine öffentliche statische Methode namens getInstance (). Das erste Mal, getInstance () aufgerufen wird, wird die Instanz Mitglied null. Die Strömung wird dann in die Schaffung Zustand fallen und eine neue Instanz der Klasse MySingleton erstellen. Nachfolgende Aufrufe von getInstance () werden feststellen, dass die Instanz-Variable bereits gesetzt ist und daher keine weitere MySingleton Instanz erstellen. Dies stellt sicher, gibt es nur eine Instanz von MySingleton, die unter allen Anrufern von getInstance geteilt wird ().

Aber diese Implementierung hat ein Problem. Multi-Threaded-Anwendungen wird eine Race-Bedingung auf die Schaffung eines einheitlichen Instanz haben. Wenn mehrere Threads einer Ausführung der getInstance getroffen () Methode bei (oder rund) zur gleichen Zeit, werden sie jeweils siehe INSTANZ Glied als null. Dies führt in jedem Thread eine neue MySingleton Instanz erstellen und anschließend das Instanz-Einstellelement.


private static MySingleton INSTANCE;

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

    return INSTANCE;
}

Hier haben wir das synchronisierte Schlüsselwort in der Methodensignatur verwendet, um die Methode getInstance () zu synchronisieren. Dies wird sicherlich unsere Race-Bedingung beheben. Themen werden nun blockieren und die Methode einer nach dem anderen ein. Aber es schafft auch ein Leistungsproblem. Nicht nur, dass diese Implementierung die Erstellung der einzelnen Instanz synchronisieren, es alle Anrufe getInstance synchronisiert (), auch liest. Liest müssen nicht synchronisiert werden, da sie einfach den Wert der Instanz zurück. Da der Großteil unserer Anrufe bilden liest (denken Sie daran, Instanziierung geschieht nur auf dem ersten Aufruf), werden wir eine unnötige Leistung entstehen getroffen durch das gesamte Verfahren synchronisiert werden.


private static MySingleton INSTANCE;

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

    return INSTANCE;
}

Hier haben wir eine Synchronisation von der Methodensignatur bewegen, zu einem synchronisierten Block, der die Erstellung der MySingleton Instanz umschließt. Aber löst das ist unser Problem? Nun, wir sind nicht mehr blockieren auf lesen, aber wir haben auch einen Schritt zurück gemacht. Mehrere Threads die getInstance () -Methode bei oder etwa zur gleichen Zeit getroffen, und sie werden alle INSTANZ Mitglied als null zu sehen. Sie werden dann drücken Sie die synchronisierten Block, wo man die Sperre erhalten wird und die Instanz erstellen. Wenn das Thread den Block verlässt, werden die anderen Threads für das Schloss kämpfen, und einer nach dem anderen wird jeder Faden durch den Block fallen und eine neue Instanz der Klasse erstellen. So sind wir 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 erneut die Kontrolle von in dem Block. Wenn das INSTANZ Mitglied bereits festgelegt wurde, werden wir Initialisierung überspringen. Dies ist doppelt überprüft Verriegelung genannt.

Das löst unser Problem von Mehrfachinstanzierung. Aber wieder einmal hat unsere Lösung eine weitere Herausforderung. Andere Themen könnten nicht „sehen“, dass das INSTANZ Mitglied aktualisiert wurde. Dies liegt daran, wie Java optimiert Speicheroperationen. Themen kopieren Sie die ursprünglichen Werte der Variablen aus dem Hauptspeicher in den Cache der CPU. Änderungen Werte werden dann geschrieben und von dieser gelesen diesem Cache. Dies ist ein Feature von Java entwickelt, die Leistung zu optimieren. Aber das schafft ein Problem für unsere Singleton-Implementierung. Ein zweiter Faden - durch eine andere CPU oder Kern verarbeitet werden, einen anderen Cache-Speicher verwendet - wird die Änderungen nicht sehen, die durch die ersten gemacht. Dadurch wird den zweiten Thread verursacht die INSTANZ Element als null zwingt eine neue Instanz der Singleton sehen geschaffen werden.


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 diese durch das Schlüsselwort volatile auf die Erklärung des INSTANZ Element verwendet. Dies wird die c sagenompiler immer las aus, und schreibt, Hauptspeicher und nicht die CPU-Cache.

Aber diese einfache Änderung kommt zu einem Preis. Weil wir die CPU-Cache umgehen, werden wir eine Leistung jedes Mal, wenn wir auf dem volatilen INSTANZ Mitglied betreiben getroffen - was wir 4 mal tun. Ich doppelt überprüfen Existenz (1 und 2), den Wert (3) und dann den Wert zurück (4). Man könnte argumentieren, dass dieser Weg der Rand Fall ist, da wir nur die Instanz beim ersten Aufruf der Methode erstellen. Vielleicht ist eine Performance-Einbußen bei der Erstellung ist erträglich. Aber auch unser Anwendungsfall, liest, wird zweimal auf dem flüchtigen Elemente arbeiten. Sobald Existenz zu überprüfen und wieder seinen Wert zurück.


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 die Leistungseinbußen aufgrund direkt vor dem flüchtigen Elemente betrieben wird, lassen Set eine lokale Variable auf den Wert der flüchtigen und auf den lokalen Variablen arbeiten, statt. Dadurch wird die Anzahl der Male, verringern wir die flüchtigen arbeiten, wodurch einige unserer verlorener Leistung Rückgewinnung. Beachten Sie, dass wir wieder unsere lokalen Variablen setzen haben, wenn wir den synchronisierten Block eingeben. Dadurch wird sichergestellt, es auf den neuesten Stand mit allen Änderungen, die aufgetreten, während wir für die Sperre warten.

Ich schrieb vor kurzem einen Artikel über diese. Die Singleton Dekonstruktion. Sie können weitere Informationen auf diesen Beispielen und ein Beispiel für die „Besitzer“ Muster dort finden. Es gibt auch ein reales Beispiel den doppelt geprüft flüchtigen Ansatz zur Schau stellt. Hoffe, das hilft.

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");
    }
}

Dies ist, wie eine einfache singleton zu implementieren:

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;
    }
}

Dies ist, wie man richtig faul Ihre singleton erstellen:

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();
    }
}

Sie müssen doppelte Kontrolle Idiom, wenn Sie die Instanz-Variable von einem laden müssen Klasse träge. Wenn Sie lazily eine statische Variable oder einen Singleton laden müssen, müssen Sie initilization auf Anfrage Halter Idiom.

Darüber hinaus, wenn der Singleton seriliazble sein muss, alle andere Felder vorübergehend und readResolve sein muss () braucht Methode, um das Singleton-Objekt unveränderlich zu halten umgesetzt werden. Andernfalls wird jedes Mal das Objekt deserialisiert, wird eine neue Instanz des Objekts erstellt wird. Was readResolve () tut, ist das neue Objekt durch readObject- () gelesen ersetzen, die gezwungen, dass neues Objekt Müll gesammelt werden, da es keine Variable darauf zu verweisen ist.

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

Verschiedene Methoden zum Singleton-Objekt zu machen:

  1. Wie pro Joshua Bloch -. Enum wäre der beste

  2. Sie können auch überprüfen Verriegelung verwendet werden.

  3. Auch innere statische Klasse verwendet werden.

Enum Singleton

Der einfachste Weg, um einen Singleton zu implementieren, der Thread-sicher ist, eine Enum mit

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

doppelt geprüft Verriegelung

Wenn Sie einen „klassischen“ Singleton kodieren mögen, die in einer Multithread-Umgebung arbeitet (ab Java 1.5) Sie diese verwenden sollte.

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 nicht Thread-sicher vor 1.5, da die Umsetzung des flüchtigen Schlüsselwort anders war.

Frühe Laden Singleton (funktioniert auch vor Java 1.5)

Diese Implementierung instanziiert das Singleton, wenn die Klasse geladen 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");
  }

}

JSE 5.0 und nehmen über den ENUM-Ansatz, sonst statischen Ansatz Singletons Halter verwenden ((a träges Laden Ansatz beschrieben von Bill Pugh). Letztere Lösung ist auch threadsicher ohne spezielle Sprachkonstrukte zu erfordern (dh flüchtige oder synchronisiert).

Ein weiteres Argument verwendet, oft gegen Singletons sind ihre Testbarkeit Probleme. Singletons sind nicht leicht mockable für Testzwecke. Wenn dies erweist sich als ein Problem zu sein, Ich mag die folgende leichte Modifikation machen:

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 zusätzliche setInstance Methode ermöglicht eine Attrappe Umsetzung der Singletonklasse während der Prüfung einstellen:

public class SingletonMock extends SingletonImpl {

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

}

Das funktioniert auch mit dem frühen Initialisierung Ansä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, diese Funktionalität in der normalen Anwendung zu auszusetzen. Andere Entwickler auf diesem Code arbeiten könnten versucht sein, die'setInstance' Methode zu verwenden, um eine bestimmte Funktion zu verändern, verändern und somit das gesamte Anwendungsverhalten zu ändern, daher sollte diese Methode enthält mindestens eine gute Warnung in seiner javadoc.

Noch für die Möglichkeit der Mockup-Tests (bei Bedarf), dieser Code Exposition kann ein akzeptabler Preis zu zahlen.

einfachste Singletonklasse

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

Ich denke immer noch nach Java 1.5, Enum die beste verfügbare Singleton Implementierung, da sie gewährleistet auch, dass selbst in der Multi-Threaded-Umgebungen -. Nur eine Instanz erstellt wird,

public enum Singleton{ INSTANCE; }

und fertig !!!

Haben Sie einen Blick auf dieses Thema.

Beispiele für GoF Design Patterns in Java-Kernbibliotheken

Von der besten Antwort der "Singleton" Abschnitt,

  

Singleton (recognizeable von creational Methoden die gleiche Instanz zurückkehrt (in der Regel von selbst) jedes Mal)

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

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

Die beste Singletonmuster ich je gesehen habe verwendet die Lieferanten-Schnittstelle.

  • Es ist generisch und wiederverwendbar
  • Sie unterstützt faul Initialisierung
  • Es ist nur synchronisiert, bis er initialisiert wurde, dann ist die Blockierung Lieferanten mit einem nicht-blockierenden Lieferanten ersetzt wird.

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();
    }
}

Manchmal genügt ein einfacher " static Foo foo = new Foo(); " ist nicht genug. Man denke nur an einige grundlegende Dateneinfügung Sie tun möchten.

Auf der anderen Seite würden Sie jede Methode synchronisieren müssen, die den Singleton-Variable als solche instanziiert. Die Synchronisation ist nicht schlecht, als solche, aber es kann zu Leistungsproblemen oder Sperren führt (in sehr seltenen Situationen mit diesem Beispiel. Die Lösung ist

public class Singleton {

    private static Singleton instance = null;

    static {
          instance = new Singleton();
          // do some of your instantiation stuff here
    }

    private Singleton() {
          if(instance!=null) {
                  throw new ErrorYouWant("Singleton double-instantiation, should never happen!");
          }
    }

    public static getSingleton() {
          return instance;
    }

}

Was passiert nun? Die Klasse wird über den Klassenlader geladen. Direkt nach der Klasse von einem Byte-Array interpretiert wurde, führt die VM den static {} - Block. das ist das ganze Geheimnis. Der statische-Block nur einmal aufgerufen wird, wird die Zeit der gegebenen Klasse (Name) des betreffenden Pakets von diesem einen Klassenlader geladen wird

public class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton(){
    if (INSTANCE != null)
        throw new IllegalStateException (“Already instantiated...”);
}

    public synchronized static Singleton getInstance() { 
    return INSTANCE;

    }

}

Als wir das Synchronized Schlüsselwort vor getInstance hinzugefügt haben, haben wir die Race-Bedingung in dem Fall vermieden, wenn zwei Threads die getInstance zugleich nennen.

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