thread - singleton java instance




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

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


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

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

Andiamo oltre il codice. Primo, vuoi che la lezione sia definitiva. 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 di usare il riflesso per creare un secondo Foo. Quindi si crea un campo private static final Foo per mantenere l'unica istanza e un metodo public static Foo getInstance() per restituirlo. Le specifiche Java assicurano 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, nonché di altri metodi o campi statici accessibili che potrebbero essere utilizzati prima che sia necessaria un'istanza, solo allora è necessario utilizzare l'inizializzazione pigra.

È possibile utilizzare una 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;
    }
}

Dal momento che la riga private static final Foo INSTANCE = new Foo(); viene eseguito solo quando viene effettivamente utilizzata la classe FooLoader, questo si occupa dell'istanza lazy 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() si readResolve() che venga restituita l'unica istanza, anche quando l'oggetto è stato serializzato in una precedente esecuzione del programma.


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


Di seguito sono 3 diversi approcci

1) Enum

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

2) Doppio controllo Bloccaggio / caricamento lento

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

Dimentica l' inizializzazione pigra , è troppo problematico. Questa è la soluzione più semplice:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

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

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 la dichiarazione volatile e difficile capire perché sia ​​necessario. Senza la volatilità, questo codice non sarebbe più sicuro per il thread grazie all'antenna antifurto a doppio controllo. Per ulteriori informazioni, consultare il paragrafo 16.2.4 di Concurrency in Practice . In breve: questo pattern (precedente a Java5.0 o senza l'istruzione volatile) potrebbe restituire un riferimento all'oggetto Bar che è (ancora) in uno stato errato.

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

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

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

Non dimenticare che Singleton è solo un Singleton per il Classloader che lo ha caricato. Se si utilizzano più caricatori (contenitori), ogni COULD dispone della propria versione di Singleton.


Se non hai bisogno di caricare 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 si desidera un caricamento lento e si desidera che Singleton sia sicuro per i thread, provare il modello di verifica doppia

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

Dato che il pattern di double check non è garantito per funzionare (a causa di qualche problema con i compilatori, non ne so più nulla.), Potresti anche provare a sincronizzare l'intero metodo getInstance o creare un registro per tutti i tuoi Singletons.


Sono confuso da alcune delle risposte che suggeriscono DI come alternativa all'uso dei singleton; questi sono concetti non correlati. È possibile utilizzare DI per iniettare istanze singleton o non-singleton (es. 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) per:

  1. Usa un framework DI come Spring, quindi
  2. Rendilo parte della tua configurazione DI se le tue dipendenze sono singleton, scope scope, scope della sessione o altro.

Questo approccio offre un'architettura disaccoppiata (e quindi flessibile e testabile) in cui utilizzare un singleton è un dettaglio di implementazione facilmente reversibile (a condizione che i singoli che usi siano sicuri, ovviamente).


Usa un enum:

public enum Foo {
    INSTANCE;
}

Joshua Bloch ha spiegato questo approccio nel suo talk Effective Java Reloaded su 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 "Effective Java" dice:

"Questo approccio è funzionalmente equivalente all'approccio del campo pubblico, tranne che è più conciso, fornisce il meccanismo di serializzazione gratuito e fornisce una garanzia ferrea contro l'istanziazione multipla, anche di fronte a sofisticati attacchi di serializzazione o riflessione. ma per essere ampiamente adottato, un tipo di enum a elemento singolo è il modo migliore per implementare un singleton . "


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


Disclaimer: ho appena riassunto tutte le risposte fantastiche e l'ho scritto nelle mie parole.

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

Il caricamento lento aggiunge bit overhead (molto onesto), quindi usalo solo quando hai un oggetto molto grande o un codice di costruzione pesante e hai anche altri metodi statici accessibili o campi che potrebbero essere utilizzati prima che sia necessaria un'istanza, quindi e solo allora è necessario utilizzare l'inizializzazione pigra. In caso contrario, scegliere il caricamento anticipato è 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. Proviamo il singleton caricato pigro

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

Fin qui tutto bene, ma il nostro eroe non sopravviverà combattendo da solo con molteplici fili del male che vogliono molti esempi del nostro eroe. Quindi, proteggilo dal multi threading malvagio

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 è abbastanza per proteggere l'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é sia ​​necessario.
Per dettagli: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

Ora siamo sicuri del filo del male, ma per quanto riguarda la serializzazione crudele? Dobbiamo assicurarci che anche durante la de-serializzazione non venga creato alcun nuovo oggetto

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() si assicurerà che l'unica istanza venga restituita, 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 voluminoso e brutto. Lasciamo che il nostro eroe faccia una fine

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 :)
Dal momento che la riga private static final Foo INSTANCE = new Foo(); viene eseguito solo quando viene effettivamente utilizzata la classe FooLoader , questo si occupa dell'istanza lazy,

ed è garantito per essere thread-safe.

E siamo arrivati ​​così lontano, ecco il modo migliore per ottenere tutto ciò che abbiamo fatto è il modo migliore 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();
}

Non c'è più paura della serializzazione, dei thread e del brutto codice. Anche ENUMS singleton viene inizializzato pigramente .

Questo approccio è funzionalmente equivalente all'approccio del campo pubblico, tranne che è più conciso, fornisce il meccanismo di serializzazione gratuito e fornisce una garanzia ferrea contro l'istanziazione multipla, anche di fronte a sofisticati attacchi di serializzazione o riflessione. Sebbene questo approccio debba ancora essere ampiamente adottato, un tipo di enum a elemento singolo è il modo migliore per implementare un singleton.

-Joshua Bloch in "Effective Java"

Ora potresti aver capito perché ENUMS è considerato il modo migliore per implementare Singleton e grazie per la tua pazienza :)
Aggiornato sul mio blog .


Enum Singleton

Il modo più semplice per implementare un Singleton protetto da thread è l'utilizzo di 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

Doppio controllo bloccato

Se si desidera codificare un singleton "classico" che funziona in un ambiente con multithreading (a partire da Java 1.5), si dovrebbe 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 di 1.5 perché l'implementazione della parola chiave volatile era diversa.

Caricamento anticipato Singleton (funziona anche prima di Java 1.5)

Questa implementazione crea un'istanza del 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");
  }

}

Continuo a pensare dopo java 1.5, enum è la migliore implementazione disponibile singleton disponibile in quanto garantisce anche che anche negli ambienti multi-threaded - viene creata solo un'istanza.

public enum Singleton{ INSTANCE; }

e hai finito !!!


Dai un'occhiata a questo post.

Esempi di modelli di progettazione GoF nelle librerie principali di Java

Dalla sezione "Singleton" della risposta migliore,

Singleton (riconoscibile dai metodi creazionali che restituiscono la stessa istanza (solitamente di per sé) ogni volta)

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

È anche possibile imparare l'esempio di Singleton dalle classi native Java stesse.


Per JSE 5.0 e versioni successive adottare l'approccio Enum, altrimenti utilizzare l'approccio statico del supporto singleton ((un approccio di caricamento pigro descritto da Bill Pugh) .L'ultima soluzione è anche thread-safe senza richiedere costrutti di linguaggio speciali (cioè volatile o sincronizzato).


Vari modi per creare oggetti singleton:

  1. Secondo Joshua Bloch - Enum sarebbe il migliore.

  2. puoi anche usare il doppio check.

  3. È possibile utilizzare anche la classe statica interna.


È necessario double-checking idioma a double-checking se è necessario caricare pigramente la variabile di istanza di una classe. Se è necessario caricare una variabile statica o un singleton pigramente, è necessario inizializzare l' idioma del detentore della domanda .

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

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

A volte un semplice " static Foo foo = new Foo();" non è abbastanza. Basti pensare all'inserimento di dati di base che vuoi fare.

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

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 classi. Subito dopo che la classe è stata interpretata da un array di byte, la VM esegue il blocco statico {} - block. questo è l'intero segreto: il blocco statico viene chiamato una sola volta, il tempo in cui la classe data (nome) del pacchetto dato viene caricata da questo caricatore di classe.


Un altro argomento spesso usato contro Singletons sono i loro problemi di testabilità. I single non sono facilmente ragguardabili a scopo di test. Se questo risulta essere un problema, mi piace apportare 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 setInstancemetodo 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");
    }

}

Questo 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 l'inconveniente di esporre questa funzionalità anche alla normale applicazione. Altri sviluppatori che lavorano su quel codice potrebbero essere tentati di usare il metodo theetInstance per alterare una funzione specifica e quindi modificare l'intero comportamento dell'applicazione, quindi questo metodo dovrebbe contenere almeno un buon avvertimento nel suo javadoc.

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


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






design-patterns