Qual è un modo efficace per implementare un modello singleton in Java? [chiuso]


Qual è un modo efficace per implementare un modello singleton in Java?

Author: Erick Robertson, 2008-09-16

30 answers

Usa un enum:

public enum Foo {
    INSTANCE;
}

Joshua Bloch ha spiegato questo approccio nel suo Efficace Java Reloaded discorso a Google I/O 2008: link al video . Vedi anche le diapositive 30-32 della sua presentazione (effective_java_reloaded.pdf):

Il modo giusto per implementare un Singleton serializzabile

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

Modifica: Una porzione online di "Java efficace" dice:

" Questo approccio è funzionalmente equivalente al pubblico l'approccio sul campo, ad eccezione del fatto che è più conciso, fornisce gratuitamente i macchinari di serializzazione e fornisce una garanzia ferrea contro più istanze, anche a fronte di sofisticati attacchi di serializzazione o riflessione. Anche se questo approccio deve ancora essere ampiamente adottato, un tipo di enum a singolo elemento è il modo migliore per implementare un singleton."

 726
Author: Stephen Denne, 2015-05-15 09:23:02

A seconda dell'utilizzo, ci sono diverse risposte "corrette".

Poiché java5 il modo migliore per farlo è usare un enum:

public enum Foo {
   INSTANCE;
}

Pre java5, il caso più semplice è:

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

Esaminiamo il codice. In primo luogo, si desidera che la classe per essere finale. In questo caso, ho usato la parola chiave final per far sapere agli utenti che è definitiva. Quindi è necessario rendere privato il costruttore per impedire agli utenti di creare il proprio Foo. Lanciare un'eccezione dal costruttore impedisce agli utenti usare reflection per creare un secondo Foo. Quindi si crea un campo private static final Foo per contenere l'unica istanza e un metodo public static Foo getInstance() per restituirlo. La specifica Java assicura che il costruttore venga chiamato solo quando la classe viene utilizzata per la prima volta.

Quando si dispone di un oggetto molto grande o di un codice di costruzione pesante E si hanno anche altri metodi o campi statici accessibili che potrebbero essere utilizzati prima che sia necessaria un'istanza, allora e solo allora è necessario utilizzare l'inizializzazione pigra.

È possibile utilizzare un private static class per caricare l'istanza. Il codice sarebbe quindi simile a:

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

Poiché la riga private static final Foo INSTANCE = new Foo(); viene eseguita solo quando viene effettivamente utilizzato il FooLoader di classe, questo si occupa dell'istanziazione pigra ed è garantito che sia thread safe.

Quando vuoi anche essere in grado di serializzare il tuo oggetto devi assicurarti che la deserializzazione non crei una copia.

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

Il metodo readResolve() assicurerà che l'unica istanza venga restituita, anche quando l'oggetto è stato serializzato in una precedente esecuzione del programma.

 219
Author: Roel Spilker, 2016-12-05 08:39:13

Disclaimer: Ho appena riassunto tutte le risposte impressionanti e l'ho scritto con parole mie.


Durante l'implementazione di Singleton abbiamo 2 opzioni
1. Caricamento lento
2. Caricamento anticipato

Lazy loading aggiunge un sovraccarico di bit (molto per essere onesti) quindi usalo solo quando hai un oggetto molto grande o un codice di costruzione pesante E hai anche altri metodi o campi statici accessibili che potrebbero essere usati prima che sia necessaria un'istanza, quindi e solo allora devi usa l'inizializzazione pigra.In caso contrario, la scelta di caricamento precoce è una buona scelta.

Il modo più semplice per implementare Singleton è

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

Tutto è buono tranne il suo singleton caricato in anticipo. Consente di provare pigro caricato 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;
    }
}

Finora tutto bene, ma il nostro eroe non sopravviverà mentre combatte da solo con più fili malvagi che vogliono molti molti esempio del nostro eroe. Quindi consente di proteggerlo dal male multi threading

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

Ma non è sufficiente per proteggere eroe, davvero!!! Questo è il meglio che possiamo / dovremmo fare per aiutare il nostro eroe

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

Questo è chiamato "Idioma di blocco a doppio controllo". È facile dimenticare la dichiarazione volatile e difficile capire perché è necessario.
Per i dettagli: http://www.cs.umd.edu / ~pugh/java/memoryModel/DoubleCheckedLocking.html

Ora siamo sicuri del filo malvagio, ma per quanto riguarda la serializzazione crudele? Dobbiamo assicurarci che anche durante la de-serializzazione non ci sia un nuovo oggetto creato

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

Il metodo readResolve() farà in modo che venga restituita l'unica istanza, anche quando l'oggetto è stato serializzato in una precedente esecuzione del nostro programma.

Finalmente abbiamo aggiunto una protezione sufficiente contro i thread e la serializzazione, ma il nostro codice sembra ingombrante e brutto. Consente di dare al nostro eroe un make over

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

Sì, questo è il nostro stesso eroe:)
Poiché la riga private static final Foo INSTANCE = new Foo(); viene eseguita solo quando viene effettivamente utilizzata la classe FooLoader, questo si prende cura del lazy istanziazione,

Ed è garantito che sia thread safe.

E siamo venuti finora, ecco il modo migliore per ottenere tutto ciò che abbiamo fatto è il miglior modo possibile

 public enum Foo {
       INSTANCE;
   }

Che internamente sarà trattato come

public class Foo {

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

Questo è tutto niente più paura della serializzazione, dei thread e del brutto codice. Inoltre Le ENUMERAZIONI singleton sono inizializzate pigramente.

Questo approccio è funzionalmente equivalente all'approccio sul campo pubblico, tranne che è più conciso, fornisce il macchinario di serializzazione gratis, e fornisce una garanzia ferrea contro più istanziazione, anche a fronte di una serializzazione sofisticata o attacchi di riflessione. Mentre questo approccio deve ancora essere ampiamente adottato, un tipo di enum a elemento singolo è il modo migliore per implementare un singleton.

- Joshua Bloch in "Java efficace"

Ora potresti aver capito perché le ENUMERAZIONI sono considerate il modo migliore per implementare Singleton e grazie per la vostra pazienza:)
Aggiornato sul mio blog.

 121
Author: xyz, 2017-05-23 10:31:37

La soluzione pubblicata da Stu Thompson è valida in Java5.0 e versioni successive. Ma preferirei non usarlo perché penso che sia soggetto a errori.

È facile dimenticare l'affermazione volatile e difficile capire perché è necessario. Senza il volatile questo codice non sarebbe più thread safe a causa dell'antipattern di blocco ricontrollato. Vedi di più su questo nel paragrafo 16.2.4 di Java Concurrency in Practice. In breve: Questo modello (prima di Java5.0 o senza il istruzione volatile) potrebbe restituire un riferimento all'oggetto Bar che si trova (ancora) in uno stato errato.

Questo modello è stato inventato per l'ottimizzazione delle prestazioni. Ma questa non è più una vera preoccupazione. Il seguente codice di inizializzazione pigro è veloce e - cosa più importante-più facile da leggere.

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

    public static Bar getBar() {
        return BarHolder.bar;
    }
}
 120
Author: Benno Richters, 2012-09-06 16:40:30

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

MODIFICA : Presta attenzione al modificatore volatile qui. :) È importante perché senza di esso, altri thread non sono garantiti dal JMM (Java Memory Model) per vedere le modifiche al suo valore. La sincronizzazione non si occupa di: serializza solo l'accesso a quel blocco di codice.

MODIFICA 2: La risposta di @ Bno descrive in dettaglio l'approccio raccomandato da Bill Pugh (FindBugs) ed è discutibile meglio. Vai a leggere e vota anche la sua risposta.

 93
Author: Stu Thompson, 2014-05-14 13:48:09

Dimentica inizializzazione pigra, è troppo problematica. Questa è la soluzione più semplice:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}
 87
Author: Jonathan, 2015-04-20 15:01:50

Assicurati di averne davvero bisogno. Fai un google per "singleton anti-pattern" per vedere alcuni argomenti contro di esso. Suppongo che non ci sia nulla di intrinsecamente sbagliato, ma è solo un meccanismo per esporre alcune risorse/dati globali, quindi assicurati che questo sia il modo migliore. In particolare, ho trovato l'iniezione di dipendenza più utile in particolare se si utilizzano anche test unitari perché DI consente di utilizzare risorse derise a scopo di test.

 45
Author: Neil Burroughs, 2014-09-11 08:27:32

Non dimenticare che il Singleton è solo un Singleton per il Classloader che lo ha caricato. Se si utilizzano più caricatori (contenitori), ognuno POTREBBE avere la propria versione del Singleton.

 25
Author: Javamann, 2008-09-16 18:12:31

Sono mistificato da alcune delle risposte che suggeriscono DI come alternativa all'utilizzo di singleton; questi sono concetti non correlati. È possibile utilizzare DI per iniettare istanze singleton o non singleton (ad esempio per thread). Almeno questo è vero se usi Spring 2.x, non posso parlare per altri framework DI.

Quindi la mia risposta all'OP sarebbe (in tutto tranne il codice di esempio più banale) a:

  1. Usa un framework DI come Spring, quindi
  2. Renderlo parte della configurazione DI se le tue dipendenze sono singleton, request scope, session scope o qualsiasi altra cosa.

Questo approccio offre una bella architettura disaccoppiata (e quindi flessibile e testabile) in cui l'uso di un singleton è un dettaglio di implementazione facilmente reversibile (a condizione che tutti i singleton utilizzati siano sicuri, ovviamente).

 20
Author: Andrew Swan, 2008-09-16 12:06:04

Considera davvero perché hai bisogno di un singleton prima di scriverlo. C'è un dibattito quasi religioso sul loro utilizzo che si può facilmente inciampare se si google singleton in Java.

Personalmente cerco di evitare singletons il più spesso possibile per molte ragioni, di nuovo la maggior parte delle quali può essere trovata su google singletons. Sento che molto spesso i single vengono abusati perché sono facili da capire da tutti, sono usati come meccanismo per ottenere dati "globali" in un OO design e sono usati perché è facile aggirare la gestione del ciclo di vita degli oggetti (o davvero pensare a come si può fare A dall'interno B). Guarda cose come Inversion of Control (IoC) o Dependency Injection (DI) per un bel middleground.

Se ne hai davvero bisogno, wikipedia ha un buon esempio di una corretta implementazione di un singleton.

 19
Author: , 2008-09-16 09:48:54

Di seguito sono riportati 3 diversi approcci

1) Enumerare

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

2) Blocco doppio controllato / Caricamento pigro

/**
* 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) Metodo statico di fabbrica

/**
* 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;
    }
}
 15
Author: Abhijit Gaikwad, 2013-02-17 03:46:40

Uso il framework Spring per gestire i miei singleton. Non impone la "singleton-ness" della classe (che non si può fare comunque se sono coinvolti più caricatori di classe) ma fornisce un modo davvero semplice per creare e configurare diverse fabbriche per la creazione di diversi tipi di oggetti.

 12
Author: Matt, 2008-09-22 20:44:55

Versione 1:

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

Caricamento pigro, filo sicuro con blocco, basse prestazioni a causa di synchronized.

Versione 2:

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

Caricamento pigro, filo sicuro con non-blocking, ad alte prestazioni.

 11
Author: coderz, 2015-06-09 04:08:16

Wikipedia ha alcuni esempi di singleton, anche in Java. L'implementazione di Java 5 sembra abbastanza completa ed è thread-safe (blocco doppio controllato applicato).

 10
Author: macbirdie, 2008-09-16 09:35:42

Se non hai bisogno di un caricamento pigro, prova semplicemente

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

Se vuoi un caricamento pigro e vuoi che il tuo Singleton sia thread-safe, prova il modello di doppio controllo

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

Poiché il modello di doppio controllo non è garantito per funzionare (a causa di qualche problema con i compilatori, non ne so nulla di più.), puoi anche provare a sincronizzare l'intero metodo getInstance o creare un registro per tutti i tuoi Singleton.

 10
Author: Aleksi Yrttiaho, 2008-09-17 04:36:50

Direi Enum singleton

Singleton l'uso di enum in Java è generalmente un modo per dichiarare enum singleton. Enum singleton può contenere variabile di istanza e metodo di istanza. Per semplicità, si noti inoltre che se si utilizza qualsiasi metodo di istanza, è necessario garantire la sicurezza del thread di tale metodo se influisce sullo stato dell'oggetto.

L'uso di un enum è molto facile da implementare e non presenta inconvenienti per quanto riguarda gli oggetti serializzabili, che devono essere elusi negli altri modi.

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

È possibile accedervi da Singleton.INSTANCE, molto più facile che chiamare il metodo getInstance() su Singleton.

1.12 Serializzazione delle costanti Enum

Le costanti Enum sono serializzate in modo diverso rispetto agli oggetti serializzabili o esternalizzabili ordinari. La forma serializzata di una costante enum consiste esclusivamente del suo nome; i valori di campo della costante non sono presenti nel modulo. Per serializzare una costante enum, ObjectOutputStream scrive il valore restituito dall'enum metodo del nome di constant. Per deserializzare una costante enum, ObjectInputStream legge il nome della costante dal flusso; la costante deserializzata viene quindi ottenuta chiamando il metodo java.lang.Enum.valueOf, passando il tipo di enum della costante insieme al nome costante ricevuto come argomenti. Come altri oggetti serializzabili o esternalizzabili, le costanti enum possono funzionare come target dei riferimenti posteriori che appaiono successivamente nel flusso di serializzazione.

Il processo mediante il quale le costanti enum sono serializzate non può essere personalizzato: qualsiasi classe specifica writeObject, readObject, readObjectNoData, writeReplace, e i metodi readResolve definiti dai tipi enum vengono ignorati durante la serializzazione e la deserializzazione. Allo stesso modo, qualsiasi dichiarazione di campo serialPersistentFields o serialVersionUID viene ignorata: tutti i tipi di enum hanno un serialVersionUID fisso di 0L. La documentazione di campi e dati serializzabili per i tipi di enum non è necessaria, poiché non vi è alcuna variazione nel tipo di dati inviati.

Citato da Oracle docs

Un altro problema con Singleton convenzionali sono che una volta che si implementa l'interfaccia Serializable, non rimangono più Singleton perché il metodo readObject() restituisce sempre una nuova istanza come costruttore in Java. Questo può essere evitato usando readResolve() e scartando l'istanza appena creata sostituendo con singleton come sotto

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

Questo può diventare ancora più complesso se la classe Singleton mantiene lo stato, poiché è necessario renderli transitori, ma con in Enum Singleton, la serializzazione è garantita da VVM.


Buona lettura

  1. Modello singolo
  2. Enumerazioni, singleton e deserializzazione
  3. Blocco ricontrollato e pattern Singleton
 8
Author: NullPoiиteя, 2017-05-23 11:55:02
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");
    }
}
 6
Author: Dheeraj Sachan, 2017-03-09 13:25:25

Potrebbe essere un po ' tardi per il gioco su questo, ma c'è un sacco di sfumature intorno all'implementazione di un singleton. Il modello di supporto non può essere utilizzato in molte situazioni. E IMO quando si utilizza un volatile-si dovrebbe anche usare una variabile locale. Iniziamo dall'inizio e ripetiamo il problema. Capirai cosa intendo.


Il primo tentativo potrebbe essere simile a questo:

public class MySingleton {

     private static MySingleton INSTANCE;

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

        return INSTANCE;
    }
    ...
}

Qui abbiamo la classe MySingleton che ha un membro statico privato chiamato ISTANZA, e un metodo statico pubblico chiamato getInstance (). La prima volta che viene chiamato getInstance (), il membro dell'ISTANZA è null. Il flusso cadrà quindi nella condizione di creazione e creerà una nuova istanza della classe MySingleton. Le chiamate successive a getInstance () troveranno che la variabile di ISTANZA è già impostata, e quindi non creare un'altra istanza MySingleton. Questo assicura che ci sia solo un'istanza di MySingleton che è condivisa tra tutti i chiamanti di getInstance ().

Ma questo l'implementazione ha un problema. Le applicazioni multi-threaded avranno una condizione di gara sulla creazione della singola istanza. Se più thread di esecuzione colpiscono il metodo getInstance () allo stesso tempo, vedranno ciascuno il membro dell'ISTANZA come null. Ciò si tradurrà in ogni thread la creazione di una nuova istanza MySingleton e successivamente impostare il membro dell'istanza.


private static MySingleton INSTANCE;

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

    return INSTANCE;
}

Qui abbiamo usato la parola chiave sincronizzata nella firma del metodo per sincronizzare Metodo getInstance (). Questo risolverà sicuramente la nostra condizione di gara. I thread ora bloccheranno e inseriranno il metodo uno alla volta. Ma crea anche un problema di prestazioni. Questa implementazione non solo sincronizza la creazione della singola istanza, ma sincronizza tutte le chiamate a getInstance(), incluse le letture. Le letture non devono essere sincronizzate in quanto restituiscono semplicemente il valore dell'ISTANZA. Poiché le letture costituiranno la maggior parte delle nostre chiamate (ricorda, l'istanziazione avviene solo alla prima chiamata), incorreremo in un risultato non necessario sincronizzando l'intero metodo.


private static MySingleton INSTANCE;

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

    return INSTANCE;
}

Qui abbiamo spostato la sincronizzazione dalla firma del metodo, a un blocco sincronizzato che avvolge la creazione dell'istanza MySingleton. Ma questo risolve il nostro problema? Bene, non stiamo più bloccando le letture, ma abbiamo anche fatto un passo indietro. Più thread colpiranno il metodo getInstance () o intorno allo stesso tempo e vedranno tutti il membro dell'ISTANZA come null. Lo faranno allora colpire il blocco sincronizzato in cui si otterrà il blocco e creare l'istanza. Quando quel thread esce dal blocco, gli altri thread si contenderanno il blocco e uno per uno ogni thread cadrà attraverso il blocco e creerà una nuova istanza della nostra classe. Quindi siamo tornati al punto di partenza.


private static MySingleton INSTANCE;

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

    return INSTANCE;
}

Qui emettiamo un altro controllo dall'INTERNO del blocco. Se il membro dell'ISTANZA è già stato impostato, salteremo l'inizializzazione. Questo è chiamato blocco doppio controllato.

Questo risolve il nostro problema di istanziazione multipla. Ma ancora una volta, la nostra soluzione ha presentato un'altra sfida. Altri thread potrebbero non "vedere" che il membro dell'ISTANZA è stato aggiornato. Ciò è dovuto al modo in cui Java ottimizza le operazioni di memoria. I thread copiano i valori originali delle variabili dalla memoria principale nella cache della CPU. Le modifiche ai valori vengono quindi scritte e lette da quella cache. Questa è una funzionalità di Java progettata per ottimizzare le prestazioni. Ma questo crea un problema per il nostro singleton attuazione. Un secondo thread, elaborato da una CPU o core diversi, utilizzando una cache diversa, non vedrà le modifiche apportate dal primo. Ciò farà sì che il secondo thread veda il membro dell'ISTANZA come null forzando la creazione di una nuova istanza del nostro singleton.


private static volatile MySingleton INSTANCE;

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

    return INSTANCE;
}

Risolviamo questo utilizzando la parola chiave volatile sulla dichiarazione del membro dell'ISTANZA. Questo dirà al compilatore di leggere sempre e scrivere sulla memoria principale e non sulla cache della CPU.

Ma questo semplice cambiamento ha un costo. Perché stiamo bypassando la cache della CPU, prenderemo un colpo di prestazioni ogni volta che operiamo sul membro ISTANZA volatile - che facciamo 4 volte. Controlliamo due volte l'esistenza (1 e 2), impostiamo il valore (3) e quindi restituiamo il valore (4). Si potrebbe sostenere che questo percorso è il caso marginale in quanto creiamo solo l'istanza durante la prima chiamata del metodo. Forse un colpo di prestazioni sulla creazione è tollerabile. Ma anche il nostro caso d'uso principale, legge, opererà sul membro volatile due volte. Una volta per controllare l'esistenza, e di nuovo per restituire il suo valore.


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

Poiché l'hit delle prestazioni è dovuto al funzionamento direttamente sul membro volatile, impostiamo una variabile locale sul valore del volatile e operiamo invece sulla variabile locale. Ciò diminuirà il numero di volte in cui operiamo sul volatile, recuperando così parte delle nostre prestazioni perse. Si noti che dobbiamo impostare nuovamente la nostra variabile locale quando entriamo nel blocco sincronizzato. Questo assicura è aggiornato con tutte le modifiche che si sono verificate mentre stavamo aspettando il blocco.

Ho scritto un articolo su questo di recente. Decostruzione del Singleton . Puoi trovare maggiori informazioni su questi esempi e un esempio del modello "holder" lì. C'è anche un esempio del mondo reale che mostra l'approccio volatile ricontrollato. Spero che questo aiuti.

 4
Author: Michael Andrews, 2017-07-12 16:06:06

È necessario ricontrollare idioma se è necessario caricare la variabile di istanza di una classe pigramente. Se è necessario caricare una variabile statica o un singleton pigramente, è necessario inizializzazione su richiesta holder idiom.

Inoltre, se il singleton deve essere seriliazble, tutti gli altri campi devono essere transitori e il metodo readResolve() deve essere implementato per mantenere l'oggetto singleton invariante. In caso contrario, ogni volta che l'oggetto viene deserializzato, un nuovo verrà creata un'istanza dell'oggetto. Ciò che fa readResolve () è sostituire il nuovo oggetto letto da readObject (), che ha costretto quel nuovo oggetto a essere garbage collection in quanto non vi è alcuna variabile che si riferisce ad esso.

public static final INSTANCE == ....
private Object readResolve() {
  return INSTANCE; // original singleton instance.
} 
 3
Author: Onur, 2011-08-02 20:49:18

Vari modi per rendere oggetto singleton:

  1. Secondo Joshua Bloch-Enum sarebbe il migliore.

  2. È possibile utilizzare il blocco doppio controllo anche.

  3. Anche la classe statica interna può essere utilizzata.

 3
Author: Shailendra Singh, 2015-08-29 12:42:30

Enumerare singleton

Il modo più semplice per implementare un Singleton che è thread-safe sta usando un Enum

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

Questo codice funziona dall'introduzione di Enum in Java 1.5

Blocco doppio controllato

Se vuoi codificare un singleton "classico" che funziona in un ambiente multithread (a partire da Java 1.5) dovresti usare questo.

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

Questo non è thread-safe prima della 1.5 perché l'implementazione della parola chiave volatile era diverso.

Caricamento precoce di Singleton (funziona anche prima di Java 1.5)

Questa implementazione istanzia il singleton quando la classe viene caricata e fornisce la sicurezza del thread.

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
Author: Dan Moldovan, 2016-05-11 17:58:05

Ecco come implementare un semplice 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;
    }
}

Questo è come creare correttamente pigro il tuo singleton:

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();
    }
}
 3
Author: Nicolas Filotto, 2016-05-29 18:50:11

Per JSE 5.0 e versioni successive adottare l'approccio Enum, altrimenti utilizzare l'approccio statico singleton holder ((un approccio di caricamento pigro descritto da Bill Pugh). Quest'ultima soluzione è anche thread-safe senza richiedere costrutti linguistici speciali (cioè volatili o sincronizzati).

 2
Author: raoadnan, 2013-05-22 18:51:33

Un altro argomento spesso usato contro i Single sono i loro problemi di testabilità. Singleton non sono facilmente mockable per scopi di test. Se questo risulta essere un problema, mi piace fare la seguente leggera modifica:

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

Il metodo setInstance aggiunto consente di impostare un'implementazione mockup della classe singleton durante il test:

public class SingletonMock extends SingletonImpl {

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

}

Funziona anche con i primi approcci di inizializzazione:

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

}

Questo ha lo svantaggio di esporre questa funzionalità al normale applicazione troppo. Altri sviluppatori che lavorano su quel codice potrebbero essere tentati di usare il metodo setInstance per alterare alterare una funzione specifica e quindi cambiare l'intero comportamento dell'applicazione, quindi questo metodo dovrebbe contenere almeno un buon avvertimento nel suo javadoc.

Tuttavia, per la possibilità di test di mockup (quando necessario), questa esposizione al codice potrebbe essere un prezzo accettabile da pagare.

 2
Author: user3792852, 2016-08-14 12:08:22

Classe singleton più semplice

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}
 0
Author: rohan kamat, 2014-05-14 13:44:14

Penso ancora che dopo java 1.5, enum sia la migliore implementazione singleton disponibile disponibile in quanto garantisce anche che anche negli ambienti multi thread venga creata una sola istanza.

public enum Singleton{ INSTANCE; }

E hai finito !!!

 0
Author: shikjohari, 2015-01-06 07:22:59

Dai un'occhiata a questo post.

Esempi di modelli di progettazione GoF nelle librerie principali di Java

Dalla sezione "Singleton" della migliore risposta,

Singleton (riconoscibile dai metodi creazionali che restituiscono la stessa istanza (di solito di se stessa) ogni volta)

  • java.lang.Runtime # getRuntime ()
  • java.oh.Desktop # getDesktop()
  • java.lang.Sistema # getSecurityManager ()

Si può anche imparare il esempio di Singleton dalle classi native Java stesse.

 0
Author: phi, 2017-05-23 12:18:27

Il miglior modello singleton che abbia mai visto utilizza l'interfaccia Fornitore.

  • È generico e riutilizzabile
  • Supporta l'inizializzazione pigra
  • È sincronizzato solo fino a quando non è stato inizializzato, quindi il fornitore di blocco viene sostituito con un fornitore non di blocco.

Vedi sotto:

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
Author: Robert Thornton, 2018-09-10 21:05:02

A volte un semplice "static Foo foo = new Foo();" non è abbastanza. Basta pensare a qualche inserimento di dati di base che si desidera fare.

D'altra parte dovresti sincronizzare qualsiasi metodo che istanzi la variabile singleton in quanto tale. La sincronizzazione non è male in quanto tale, ma può portare a problemi di prestazioni o blocco (in situazioni molto rare usando questo esempio. La soluzione è

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

}

Ora cosa succede? La classe viene caricata tramite il caricatore di classe. Subito dopo la classe era interpretato da un array di byte, la VM esegue il static {} - block. questo è l'intero segreto: il blocco statico viene chiamato solo una volta, il tempo in cui la classe data (nome) del pacchetto dato viene caricata da questo caricatore di una classe.

 -3
Author: Georgi, 2013-04-26 06:03:05
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;

    }

}

Poiché abbiamo aggiunto la parola chiave Synchronized prima di getInstance, abbiamo evitato la condizione di gara nel caso in cui due thread chiamino getInstance contemporaneamente.

 -5
Author: somenath mukhopadhyay, 2014-05-14 13:44:05