[Design patterns in Java] Singleton

Definizione

Il pattern Singleton garantisce che per una classe ci sia una sola istanza e fornisce un unico punto di accesso alla stessa.

Classificazione

Il Singleton è classificato tra i pattern creazionali.

Problema e campo di applicazione

Vogliamo che una classe denominata Singleton (ma che possiamo rinominare nel modo più opportuno) abbia al più un’istanza e che si possa accedere a quest’ultima in maniera univoca.

Tale necessità può presentarsi in diverse occasioni: accesso ai database, configurazioni, logging ecc.

Soluzione

Per prima cosa occorre evitare che si possano creare istanze della classe, per cui rendiamo privato il costruttore con

private Singleton();

L’unica istanza disponibile dovrà essere rappresentata da una variabile privata statica di tipo Singleton.

La variabile è privata perchè deve essere accessibile attraverso un opportuno metodo e statica in quanto tale metodo è statico (legato alla classe e non ad una singola istanza).

private static Singleton instance;

Dobbiamo creare un metodo pubblico e statico getInstance() che restituirà la variabile statica suddetta, effettuando l’inizializzazione alla prima richiesta.

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

Questa soluzione iniziale presenta un grosso difetto, ovvero non è thread-safe.

In parole povere, in un ambiente multi-threading, più thread possono accedere alla classe e nella fase di inizializzazione (quando l’istanza non è ancora disponibile) possono creare più oggetti, violando il vincolo che è alla base del pattern.

Ad esempio supponiamo che ci siano in esecuzione due thread t1 e t2 e che l’istanza della classe Singleton non sia stata ancora creata.

Il thread t1 richiama il metodo getInstance() ma l’esecuzione viene sospesa subito dopo aver verificato che la variabile instance è impostata a null. Intanto il thread t2 richiama lo stesso metodo, completando l’esecuzione del codice e istanziando un oggetto.

A questo punto t1 viene riattivato e a sua volta crea un’ulteriore istanza.

In definitiva ci sono due oggetti della classe Singleton. E questo non va bene.

Quali sono le possibili soluzioni?

Osserviamo che la creazione dell’istanza è di tipo lazy ovvero posticipata al momento in cui viene richiesta per la prima volta tramite la getInstance().

Se il metodo non viene mai invocato non ci sarà alcun oggetto della classe Singleton.

Possiamo creare l’istanza al caricamento della classe dal parte del classloader con la seguente modifica

private static Singleton instance = new Singleton();

Una soluzione semplice da realizzare ma con lo svantaggio di creare l’istanza anche se questa non dovesse mai essere richiesta/utilizzata.

In alternativa possiamo rendere synchronized il metodo getInstance() a costo di impattare sulle performance dell’applicazione in quanto la sincronizzazione, che introduce un overhead dovuto all’acquisizione del lock, sarebbe applicata ad ogni invocazione del metodo stesso.

Meglio spostare la sincronizzazione nel blocco if in corrispondenza della creazione dell’istanza, così che venga applicata una sola volta, effettuando allo stesso tempo un doppio controllo.
Questo rende il codice un po’ più complesso e richiede che la variabile instance sia dichiarata come volatile per essere condivisa correttamente tra i vari thread.

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

Il codice di esempio è disponibile su GitHub.

INDICE DESIGN PATTERNS

Lascia una risposta

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *

Utilizzando il sito, accetti l'invio dei cookies da parte nostra. Maggiori informazioni

Questo sito utilizza i cookies per fornire la migliore esperienza di navigazione possibile. Continuando ad utilizzarlo senza modificare le impostazioni o cliccando su "Accetta" acconsenti al loro utilizzo.

Chiudi