Padroneggia il pattern singleton in Java con la nostra guida sulla sicurezza dei thread, i test e alternative moderne come l'iniezione delle dipendenze per codice manutenibile e scalabile.
January 28, 2026 (3mo ago)
Guida al pattern Singleton in Java per codice pulito
Padroneggia il pattern singleton in Java con la nostra guida sulla sicurezza dei thread, i test e alternative moderne come l'iniezione delle dipendenze per codice manutenibile e scalabile.
← Back to blog
Guida al pattern Singleton in Java per codice pulito
Sommario: Padroneggia il pattern singleton in Java con la nostra guida sulla sicurezza dei thread, i test e alternative moderne come l'iniezione delle dipendenze per codice manutenibile e scalabile.
Introduzione
Il Pattern Singleton in Java impone che una classe abbia una sola istanza e fornisce un unico punto di accesso globale ad essa. Questo è utile per oggetti come gestori di configurazione, pool di connessioni o servizi centrali di logging dove più istanze causerebbero stato incoerente o spreco di risorse. Capire come implementare un Singleton sicuro e testabile — e quando evitarlo — è essenziale per codice Java pulito e manutenibile.
Comprendere il design pattern Singleton

Un'analogia utile è la torre di controllo del traffico aereo di un aeroporto. Non costruisci una torre separata per ogni aereo; ogni aereo comunica con la stessa torre. La torre è la singola fonte di verità. Questo è il ruolo che un Singleton svolge in un'applicazione.
Il pattern è stato reso popolare nella letteratura classica sui design pattern e nei sistemi enterprise Java1. Le sue responsabilità principali sono semplici:
- Garantire una singola istanza — tipicamente rendendo il costruttore privato.
- Fornire un punto di accesso globale — di solito un metodo statico come getInstance().
Caratteristiche chiave
- Una sola istanza: la classe impedisce la creazione di più di un'istanza.
- Costruttore privato: impedisce l'instanziazione diretta da altre classi.
- Punto di accesso globale: un metodo statico restituisce la singola istanza.
- Ciclo di vita autocontenuto: la classe gestisce la propria istanza.
Punto chiave: il Singleton impone un unico oggetto e un accesso globale controllato affinché diverse parti di un'applicazione parlino con la stessa identica istanza.
Implementare Singletons thread-safe in Java

Un Singleton pigramente inizializzato ingenuo è semplice ma non thread-safe. Considera questo esempio di base:
public class BasicLazySingleton {
private static BasicLazySingleton instance;
private BasicLazySingleton() {}
public static BasicLazySingleton getInstance() {
if (instance == null) {
instance = new BasicLazySingleton();
}
return instance;
}
}
In un ambiente multithread, due thread possono osservare instance == null e creare entrambi una nuova istanza. Una soluzione semplice è sincronizzare getInstance(), ma ciò causa un lock non necessario a ogni chiamata.
Metodo sincronizzato (funziona, ma è costoso)
public class SynchronizedSingleton {
private static SynchronizedSingleton instance;
private SynchronizedSingleton() {}
public static synchronized SynchronizedSingleton getInstance() {
if (instance == null) {
instance = new SynchronizedSingleton();
}
return instance;
}
}
Questo risolve la sicurezza dei thread ma penalizza le prestazioni perché la sincronizzazione viene eseguita ad ogni accesso.
Bill Pugh (Initialization-on-demand Holder)
Un approccio più pulito è l'idioma Initialization-on-demand Holder. Offre inizializzazione lazy e sicurezza dei thread senza l'overhead della sincronizzazione perché l'inizializzazione delle classi è thread-safe nella JVM2.
public class BillPughSingleton {
private BillPughSingleton() {}
private static class SingletonHolder {
private static final BillPughSingleton INSTANCE = new BillPughSingleton();
}
public static BillPughSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
Questo si affida alla JVM per caricare SingletonHolder solo quando viene chiamato getInstance(), e le garanzie sull'inizializzazione delle classi forniscono la sicurezza dei thread.
Enum Singletons (raccomandato)
Joshua Bloch raccomanda di usare un enum a elemento singolo per un Singleton. Questo è conciso e protegge da attacchi via reflection e serializzazione3.
public enum EnumSingleton {
INSTANCE;
public void someMethod() {
// business logic
}
}
Vantaggi:
- Codice minimo
- Sicurezza dei thread fornita dalla JVM
- Sicurezza nella serializzazione
- Forte protezione contro la creazione di istanze via reflection
Per la maggior parte dei casi, un enum Singleton è la scelta più robusta e manutenibile.
I costi nascosti del pattern Singleton

Sebbene i Singleton risolvano un problema particolare, introducono costi nascosti: accoppiamento stretto, stato globale e ridotta testabilità.
Quando il codice invoca Singleton.getInstance(), quella dipendenza è nascosta. Il contratto pubblico della classe non rivela che essa fa affidamento su un oggetto globale. Questo porta a:
- Codice rigido difficile da modificare
- Test fragili o che richiedono la vera istanza globale
- Difficoltà a eseguire test in parallelo a causa dello stato condiviso
Problemi di testing
I Singleton rendono difficile il testing unitario isolato. Non puoi facilmente sostituire un mock al suo posto, quindi i test spesso usano l'implementazione reale. Questo può causare test lenti, accoppiamento accidentale a sistemi esterni e pipeline CI fragili.
Una classe che dipende da un Singleton nasconde quella dipendenza dalla sua firma, rendendo il codice più difficile da comprendere e manutenere.
Stato globale e dipendenze nascoste
Un Singleton è essenzialmente una variabile globale. Lo stato globale oscura il flusso di informazioni e crea interdipendenze difficili da districare. Questo complica il debug e rallenta lo sviluppo.
Per approfondire anti-pattern comuni e manutenibilità, consulta la nostra guida sui design pattern nella OOP e sulle strategie di testing.
Alternative moderne ai Singleton
Con l'evoluzione dei sistemi, gli sviluppatori hanno adottato pattern che evitano i problemi del Singleton preservando il controllo sulla creazione degli oggetti.
Dependency Injection
La Dependency Injection (DI) inverte la responsabilità: i client dichiarano le loro dipendenze e un contenitore esterno le fornisce. Questo rende le dipendenze esplicite e facili da sostituire nei test. Framework DI come Spring e Guice gestiscono per te i cicli di vita degli oggetti e il wiring4.
Vantaggi della DI:
- Decoupling — i componenti dipendono da astrazioni, non da classi concrete
- Testabilità — puoi iniettare mock o fake
- Flessibilità — scambia implementazioni tramite configurazione
Questo si allinea con le best practice di inversion of control e produce sistemi più chiari e manutenibili7.
Factory
Il pattern Factory centralizza la logica di creazione. Una factory può restituire la stessa istanza o nuove istanze a seconda delle necessità. Il codice client chiede un oggetto alla factory senza sapere come viene creato, mantenendo il codice modulare e testabile.
Istanza con ambito (Scoped Instances)
A volte serve una singola istanza, ma solo entro uno scope limitato (request, session o application). I framework supportano bean a request-scoped o session-scoped per fornire un bilanciamento tra riuso delle risorse e isolamento.
Singleton nei sistemi distribuiti
Un Singleton locale alla JVM non ti garantisce una singola istanza attraverso più istanze del servizio. I microservizi eseguono più JVM, quindi hai bisogno di soluzioni distribuite per lo stato condiviso, come Redis o un servizio di configurazione centralizzato come Consul o Spring Cloud Config6.
Come refattorizzare i Singleton dal codice legacy

Refattorizzare i Singleton richiede cautela. Il problema principale è la dipendenza diretta sulla chiamata statica getInstance(). Un approccio graduale e metodico riduce il rischio.
Strategia passo-passo:
- Introduci un'interfaccia che descriva i metodi pubblici del Singleton e fai in modo che il Singleton la implementi.
- Rendi le dipendenze esplicite aggiungendo parametri al costruttore nelle classi che usano il Singleton.
- Usa una factory o un contenitore DI (Spring, Guice) per fornire l'implementazione come istanza gestita.
- Sostituisci le chiamate a Singleton.getInstance() con dipendenze iniettate tramite costruttore.
- Rimuovi il codice specifico del Singleton una volta che tutti i chiamanti ricevono la dipendenza esternamente.
Vantaggi: migliore modularità, testabilità e chiarezza. Refattorizzare i Singleton trasforma un codebase rigido in componenti flessibili e testabili.
Domande frequenti
Il pattern Singleton è un anti-pattern?
Spesso, sì. Il Singleton introduce stato globale e accoppiamento stretto, riducendo la testabilità e aumentando il costo di manutenzione a lungo termine. Usalo con parsimonia e preferisci DI o istanze con scope quando possibile.
Come può essere rotto un Singleton in Java?
Reflection e serializzazione possono creare nuove istanze a meno che non ti difenda esplicitamente. L'uso di un enum per un singleton evita queste insidie3.
Un Singleton è appropriato per i microservizi?
No. Un Singleton è limitato alla JVM, quindi ogni istanza del servizio avrà il proprio Singleton. Per stato condiviso tra servizi, usa sistemi distribuiti come Redis o servizi di configurazione centralizzati6.
Tre sintesi Q&A concise
Q: Quando dovrei usare un Singleton? A: Solo quando una singola istanza rappresenta veramente una risorsa unica e globale all'interno di una singola JVM e non esiste un'alternativa migliore. Preferisci enum Singleton se devi usarlo.
Q: Come creo un Singleton lazy e thread-safe? A: Usa l'idioma Initialization-on-demand Holder (Bill Pugh) o un enum. Entrambi forniscono sicurezza dei thread con overhead minimo; l'enum protegge anche da problemi di serializzazione e reflection23.
Q: Cosa dovrei usare invece dei Singleton per una migliore testabilità? A: Usa Dependency Injection, factory o istanze con scope in modo che le dipendenze siano esplicite e facilmente sostituibili nei test47.
L'AI scrive codice.Tu lo fai durare.
Nell'era dell'accelerazione AI, il codice pulito non è solo una buona pratica — è la differenza tra sistemi che si scalano e codebase che collassano sotto il loro stesso peso.