2013-04-03 4 views
19

Stavo facendo qualche ricerca sui singleton, in particolare per quanto riguarda l'inizializzazione pigro vs bisognosa di singleton.Schema singleton con combinazione di caricamento lazy e sicurezza thread

Un esempio di inizializzazione ansioso:

public class Singleton 
{ 
    //initialzed 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; 
    } 
} 

ma come mostrato sopra che è pronto inizializzazione e sicurezza filo viene lasciata JVM ma ora, voglio avere questo stesso modello ma con inizializzazione ritardata.

quindi venire con questo approccio:

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

Come mostrato sopra Poiché la linea

private static final Foo INSTANCE = new Foo(); 

viene eseguita solo quando il FooLoader classe viene effettivamente utilizzato, questo prende cura della istanziazione pigrizia , ed è garantito per essere thread-safe.

È corretto?

+0

Duplicato http://stackoverflow.com/q/5842273/823393 – OldCurmudgeon

+1

Questa non è un'inizializzazione davvero pigra? L'istanza può essere caricata prima che venga invocato il metodo 'getInstance'. – Perception

+0

Questo è certamente pigro. L'istanza è solo istanziata al primo riferimento della classe. A cui verrà fatto riferimento solo nella prima invocazione di 'getInstance'. Anche se rimuoverei il controllo della creazione nel costruttore. –

risposta

17

Il secondo snippet di codice è, a mio avviso, il modo migliore di inizializzare pigramente un singolo singolarmente. In realtà ha un nome di modello

Initialization-on-demand holder idiom

Vorrei suggerire lo si utilizza.

+0

Grazie mille. approfondito l'articolo perfetta spiegazione –

+0

Rispondere al comportamento di caricamento della classe della JVM piuttosto che rendere la tua pigrizia suoni espliciti per me come una cattiva idea. Probabilmente non c'è nemmeno un vantaggio in termini di prestazioni rispetto al modo semplice. – einpoklum

+0

@einpoklum Ho visto il tuo esempio usato DCL + volatile. Anche se è corretto (dichiarandolo volatile) preferirei usare il suo ultimo esempio della DCL. Lo trovo più leggibile e meno prolisso. Infine, il tuo esempio è sbagliato, dovresti sincronizzare su "Foo.class' e non sull'istanza. –

1

Il modo migliore è effettivamente usare il Enum Way:

public enum Singleton { 
    INSTANCE; 
    public void execute (String arg) { 
      //... perform operation here ... 
    } 
} 
+1

Direi che è il peggior modo ... e Bloch dovrebbe rimborsare i suoi lettori :) – ZhongYu

+0

Ehi lo so: il ragazzo è cattivo ... –

+0

+1 zhong.j.yu Anch'io non mi è mai piaciuto questo modello anche se Bloch parlava così tanto e poi tutti gli altri lo hanno seguito:/ –

2

Il secondo è molto male in termini di leggibilità, prima è adatta. Dai un'occhiata a questo article. Riguarda il doppio controllo del blocco, ma ti darà anche informazioni dettagliate sul multithreading dei singleton.

+0

Com'è il secondo * pessimo *? –

+0

Funzionerà, ma non vedo alcun uso in FooLoader e se l'istruzione all'interno del costruttore. Lascia che non sia male ma sovraccarichi. Ricorda che altre persone leggeranno il tuo codice. Quindi, tienilo semplice. – Mikhail

+0

Sono d'accordo, il check in del costruttore non è necessario e dovrebbe essere rimosso. Ma quando si parla di sicurezza dei thread, non vedo assolutamente nulla di sbagliato nel secondo frammento di codice. –

5

Il tuo primo progetto è in realtà pigro. Pensaci, l'istanza viene creata solo quando la classe è inizializzata; la classe viene inizializzata solo quando viene chiamato il metodo getSingleton() [1]. Quindi l'istanza viene creata solo quando viene richiesta, cioè è creata pigramente.

[1] http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1

+0

+1 hai ragione è ancora pigro anche se immagino che lo farebbe avere altri metodi disponibili in cui un diverso riferimento statico potrebbe crearlo. –

+0

È improbabile, tuttavia, che una classe singleton contenga metodi statici (diversi da 'getInstance()') – ZhongYu

+2

ma non è improbabile che contenga un campo statico pubblico :) –

0

A mio parere, si tratta di un modello inadeguato per l'uso. Fa supposizioni sul comportamento della JVM che sono non banali e confuse. Inoltre, ha una classe fittizia. Le classi fittizie dovrebbero essere evitate quando possibile.

suggerisco l'approccio diretto:

public class Foo { 
    private volatile static final Foo instance = null; 

    private Foo() { 
    } 

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

... anche se, questo non funziona così com'è - non è thread-safe .. cosa si vuole veramente è il modello doppio controllo presentato al punto 71 di Bloch Java efficace; vedi here.Adattamento dell'esempio al link al vostro caso, otteniamo:

public class Foo { 
    private volatile static final Foo instance = null; 

    private Foo() { 
    } 

    public static Foo instance() { 
     if (instance != null) return instance; 
     synchronized(instance) { 
      Foo result = instance; 
      if (instance == null) { 
       result = instance = new Foo(); 
      return result; 
     } 
    } 
} 

Note:

  • Non preoccuparti per le prestazioni di questo codice, le JVM moderni prendono cura di esso ed è bene. Dopo tutto, premature optimization is the root of all evil.
  • Come suggerito in altre risposte, quanto sopra non è la soluzione preferita di Bloch, ma penso che l'uso di un enum per un singleton sia semanticamente inappropriato proprio come l'OP in origine.
+1

Se non si è preoccupati delle prestazioni, utilizzare solo il primo esempio e sincronizzare l'intero metodo. Ancora più semplice! –

+1

Per versioni alternative del doppio blocco, consultare https://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java –