2009-12-10 16 views
12

Il classico di scrivere un Singleton in Java è come questo:Diversi modi per scrivere Singleton in Java

public class SingletonObject 
{ 
    private SingletonObject() 
    { 
    } 

    public static SingletonObject getSingletonObject() 
    { 
     if (ref == null) 
      // it's ok, we can call this constructor 
      ref = new SingletonObject(); 
     return ref; 
    } 

    private static SingletonObject ref; 
} 

e possiamo aggiungere parola chiave sincronizzato se ne abbiamo bisogno per l'esecuzione in casi multithread.

ma preferisco scrivere come:

public class SingletonObject 
{ 
    private SingletonObject() 
    { 
     // no code req'd 
    } 

    public static SingletonObject getSingletonObject() 
    { 
     return ref; 
    } 

    private static SingletonObject ref = new SingletonObject(); 
} 

che credo sia più conciso, ma stranamente non ho visto alcun codice di esempio scritto in questo modo, non v'è alcun effetto male se ho scritto il mio codice in questo modo?

+0

Nel secondo caso, dove stai inizializzando staticamente 'singletonObject', puoi (e dovresti) renderlo' final' pure, per evitare modifiche accidentali. (Tendo a preferire anche l'inizializzazione statica, per questo motivo.) – Wyzard

risposta

18

La differenza tra il codice e il "codice di esempio" è che il singleton viene istanziato quando la classe viene caricata, mentre nella versione "campione" non viene istanziata finché non è effettivamente necessaria.

+0

si spiega chiaramente questo problema – gleery

+5

Ovviamente la differenza è in genere irrilevante poiché le classi java vengono caricate su richiesta. –

+0

Inoltre, non è necessario sincronizzare l'inizializzazione statica. – jasxir

9

Bene, in quest'ultimo caso l'oggetto singleton viene creato prima che sia mai necessario, ma nella maggior parte dei casi ciò probabilmente non è terribilmente male.

A proposito, Joshua Bloch raccomanda (in Effective Java, 2a ed, punto 3) attuare singletons utilizzando un unico elemento enum:

public enum SingletonObject { 
    INSTANCE; 
} 

Egli dà la seguente giustificazione:

[...] è più conciso, fornisce la serializzazione di macchinari gratuitamente e fornisce una garanzia di ferro da stiro contro più istanze , anche in presenza di serializzazione sofisticata o attacchi di riflessione . Mentre questo approccio deve ancora essere ampiamente adottato, un tipo di enum a elemento singolo è il miglior modo per implementare un singleton.

+0

Ratti, c'è una seconda edizione? (badabing) –

+1

Ho letto alcuni capitoli di questo libro molto tempo fa, forse dovrei leggere attentamente questa volta – gleery

+1

Non sono sicuro che sia significativo fornire "una garanzia ironclad contro l'istanziazione multipla, anche di fronte a ... attacchi di riflessione ". Se un utente malintenzionato è in grado di eseguire un attacco di riflessione, possiede completamente la VM e non c'è davvero nulla che tu possa fare al riguardo. È come se quadruplicasse la porta principale e mettesse su di essa un sofisticato sistema di allarme, lasciando le finestre spalancate. –

5

Direi che quest'ultimo codice è il modello più standard, in realtà. La tua prima versione non è thread-safe. I modi di renderlo thread-safe includono la sincronizzazione su ogni accesso, o con molta attenzione facendolo usare il blocco a doppio controllo (che è sicuro come il modello di memoria Java 5, a patto che sia corretto).

Si noti che a causa del fatto che le classi vengono inizializzate pigramente, il vostro ultimo codice creerebbe comunque inutilmente un oggetto solo se avete chiamato metodi statici sulla classe senza voler creare l'istanza.

C'è uno schema che usa una classe nidificata per eseguire l'inizializzazione che può rendere questo pigro, ma personalmente la seconda forma quasi sempre fa abbastanza bene per me da solo.

Ci sono ulteriori dettagli di questo in Java efficace, ma non ce l'ho con me per trovare il numero dell'articolo, temo.

+0

+1 per accenno a problemi con Double Checked Locking. -1 per aver perso l'opportunità di evidenziare i pericoli dell'utilizzo di singletons in primo luogo ;-) – philsquared

+0

grazie, davvero buona risposta – gleery

2

Penso che il tuo problema sia che stai mescolando un'inizializzazione pigra e singleton.Un singoletto può essere implementato con diverse strategie di inizializzazione:

  • inizializzazione di classe di carico
  • artificiale inizializzazione che utilizza ricontrollato bloccaggio
  • inizializzazione differita con singolo controllo (con eventuale inizializzazione ripetuta)
  • artificiale inizializzazione che utilizza il caricatore di classe (idioma di classe titolare)

Tutti questi approache s sono discussi in Effective Java 2nd Item 71: Use lazy initialization judiciously.

+0

Lo leggerò più tardi, :) – gleery

-2

Sono d'accordo con Anon, e nel caso in cui ho sempre voglia di istanziare il singleton userei

public class SingletonObject 
{ 
public static SingletonObject REF = new SingletonObject(); 

private SingletonObject() 
{ 
    // no code req'd 
} 

}

+1

Pericolo: REF è un campo pubblico non finale. Potrebbe essere sostituito da sottoclassi. Questo modello avrà anche problemi se SingletonObject è serializzabile. Basta fare la cosa di Java efficace, è altrettanto facile e tutto ciò da evitare. – Barett

+0

@Barett -Penso che Singleton non possa essere sottoclasse perché ha sottostrutture private constructor.so verrà richiesto di richiamare esplicitamente il costruttore super-classe. – AKS

+0

Le sottoclassi interne sono improbabili ma possibili. (BTW possono essere creati in fase di esecuzione a seconda dell'ambiente) – Barett

18

Nel secondo modulo, il tuo Singleton è avidamente caricati e questo è in realtà la forma preferita (e la prima non è thread-safe come hai detto tu stesso). eager loading non è una cosa negativa per il codice di produzione, ma ci sono contesti in cui si potrebbe desiderare di carico pigri tuoi single, come discusso dall'autore di Guice, Bob Lee, in Lazy Loading Singletons che sto citando qui di seguito:

In primo luogo, perché dovresti voler caricare pigro un singleton? In produzione, in tipicamente desidera caricare avidamente tutte tuoi single così si cattura errori presto e prendere qualsiasi prestazione ha colpito fino fronte, ma nei test e durante lo sviluppo, si desidera solo caricare ciò è assolutamente così bisogno di non a tempo di scarto.

Prima di Java 1.5, ho caricato in modo pigro singletons utilizzando pianura vecchio sincronizzazione, semplice ma efficace:

static Singleton instance; 

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

Le modifiche apportate al modello di memoria di 1.5 abilitato il famigerato ricontrollato Locking (DCL) idioma . Per implementare DCL, si controlla un campo volatile nel percorso comune e sincronizzare solo quando necessario, :

static volatile Singleton instance; 

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

Ma volatile non è che molto più veloce di quanto synchronized, synchronized è piuttosto veloce al giorno d'oggi, e DCL richiede altro codice, quindi anche dopo 1.5 è uscito, Ho continuato a utilizzare la semplice sincronizzazione vecchia.

Immaginate la mia sorpresa oggi, quando Jeremy Manson mi indicò la Initialization on Demand Holder (IODH) idiom che richiede molto poco codice e ha a zero spese generali sincronizzazione. Zero, come in ancora più veloce di volatile. IODH richiede lo stesso numero di righe del codice come semplice sincronizzazione precedente e è più veloce di DCL!

IODH utilizza l'inizializzazione classe pigra . La JVM non eseguirà lo inizializzatore statico di una classe finché non si tocca effettivamente qualcosa nella classe con lo . Questo vale anche per le classi nidificate statiche, . Nel seguente esempio, il JLS guarantees la JVM non verrà inizializzato instance fino a quando qualcuno chiamate getInstance():

static class SingletonHolder { 
    static Singleton instance = new Singleton();  
} 

public static Singleton getInstance() { 
    return SingletonHolder.instance; 
} 

[...]

Aggiornamento: di credito che è di Cesare, Effective Java (diritti d'autore 2001) dettagliato questo modello sotto la voce 48. Va avanti a sottolineare che è ancora necessario utilizzare la sincronizzazione o DCL in contesti non statici.

Ho anche acceso movimentazione Singleton in mio quadro dalla sincronizzazione di DCL e vide un'altra performance incremento del 10% (rispetto a prima ho iniziato utilizzando la riflessione veloce di cglib). Ho solo usato un thread nel mio micro-benchmark, così la spinta alla concorrenza potrebbe essere ancora maggiore dato che ho sostituito un serratura fortemente sostenuto con un relativamente fine accesso grana campo volatili .

Nota che Joshua Bloch raccomanda ora (dal Effective Java, 2a ed) per implementare single con un unico elemento enum come sottolineato da Jonik.

+1

Uno svantaggio del modello IDOH è che se c'è un'eccezione lanciata nel costruttore non verrà visualizzata nella traccia dello stack, rendendo così difficile la manutenzione. Riceverai solo java.lang.ExceptionInInitializerError – barbazoo

1

il secondo modo risolverà il problema di multi-threading ma creerà sempre il Singleton anche quando non ne hai bisogno. Il modo migliore per farlo è creare una classe nidificata.

public class singleton 
{ 
    private singleton() 
    { 
     System.out.println("I'am called only when it's needed"); 
    } 

    static class Nested 
    { 
     Nested() {} 
     private static final singleton instance = new singleton(); 
    } 

    public static singleton getInstance() 
    { 
     return Nested.instance; 
    } 

    public static void main(String [] args) 
    { 
     singleton.getInstance(); 
    } 
} 
+0

[Convenzioni del codice per il linguaggio di programmazione Java] (http://www.oracle.com/technetwork/java/javase/documentation/codeconvtoc-136057.html) –

0

Il modo migliore per scrivere una classe singleton è riportata qui sotto prego provare

public final class SingeltonTest { 
    /** 
    * @param args 
    * @return 
    */ 
    private static SingeltonTest instance = null; 

    private SingeltonTest() { 
     System.out.println("Rahul Tripathi"); 
    } 

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

Diversi modi di attuare singleton pattern in Java è come segue

public class SingletonClass { 
private SingletonClass= null;  
public static SingletonClass getInstance() { 
    if(SingletonClass != null) { 
     SingletonClass = new SingletonClass(); 
    } 
    } 
} 

Questo non è thread sicuro. I seguenti sono thread safe attuazione del modello di progettazione Singleton

1. Draconiana sincronizzazione

private static YourObject instance; 

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

2.Double sincronizzazione controllo

Reference

private static final Object lock = new Object(); 
private static volatile YourObject instance; 

public static YourObject getInstance() { 
    YourObject r = instance; 
    if (r == null) { 
     synchronized (lock) { // While we were waiting for the lock, another 
      r = instance;  // thread may have instantiated the object. 
      if (r == null) { 
       r = new YourObject(); 
       instance = r; 
      } 
     } 
    } 
    return r; 
} 

3.Inizializzazione-on-demand supporto linguaggio

Reference

public class Something { 
    private Something() {} 

    private static class LazyHolder { 
     static final Something INSTANCE = new Something(); 
    } 

    public static Something getInstance() { 
     return LazyHolder.INSTANCE; 
    } 
} 

4. altro è usando enum

public enum Singleton { 
    SINGLE; 
    public void myMethod(){ 
    } 
} 
0
// Lazy loading enabled as well as thread safe 

class Singleton {

   private static class SingletonHolder { 
       public static Singleton instance = new Singleton(); 
       } 

       public static Singleton getInstance() { 
        return SingletonHolder.instance; 
        } 
       } 
Problemi correlati