Ci sono diverse soluzioni, eccole mostrate in dettaglio:
Caso 1)
codice:
class Singleton1
{
private static Singleton1 instance = new Singleton1 ();
private Singleton1 ()
{
// .... blabla ....
}
public static Singleton1 getInstance ()
{
return instance;
}
}
La istanza del singleton viene creata quando la classe viene caricata dalla JVM. In questo caso il synchronized non serve. La istanza è già creata e basta semplicemente ritornare il reference.
Caso 2)
codice:
class Singleton2
{
private static Singleton2 instance;
private Singleton2 ()
{
// .... blabla ....
}
public static synchronized Singleton2 getInstance ()
{
if (instance == null)
instance = new Singleton2 ();
return instance;
}
}
La istanziazione del singleton è fatta in modo "lazy" (solo alla prima richiesta). Viene usato il synchronized per poter fare la inizializzazione in modo "atomico", quindi thread-safe.
Caso 3)
codice:
class Singleton3
{
private static class InstanceHolder
{
public static Singleton3 instance = new Singleton3 ();
}
private Singleton3 ()
{
// .... blabla ....
}
public static Singleton3 getInstance ()
{
return InstanceHolder.instance;
}
}
Questa è ancora una inizializzazione "lazy" ma non usa synchronized. Sfrutta il fatto che la JVM esegue la inizializzazione dei campi static di una classe in modo "synchronized" una volta sola quando la classe viene caricata.
C'è una classe nested (quindi static) InstanceHolder. Pur essendo dentro la classe Singleton3, non viene caricata nello stesso momento in cui viene caricata Singleton3.
Nel caso sopra, InstanceHolder viene caricata solo quando viene "usata" per la prima volta, cioè quando si chiede il suo campo 'instance'.