Java: Thread-safe Serializable Singleton
Jak udělat thread-safe serializable singleton v Javě.
Obvyklý způsob, využívající synchronize a různé kontroly, typicky Double Checked Locking, neřeší problém s thread-safety, protože prostě není nijak zaručeno pořadí provádění instrukcí. Jediné místo, kdy JVM zaručuje vláknovou bezpečnost (ehm :-) je při statické inicializaci načítaných tříd, takže:
Eager loading (při prvním použití třídy Singleton se inicializuje INSTANCE):
Lazy loading (INSTANCE se inicializuje až při volání metody getInstance(), která používá vnitřní třídu):
V nadpisu je i zmínka o serializaci. Problém je v tom, že při deserializaci singletonu se fakticky nepoužije správná instance singletonu, ale udělá se nová. Toto jde řešit pomocí readResolve() a lze velmi dobře použít u enumerací dělaných pomocí singletonu.
Přičemž readResolve() je normální metoda, která může sahat na položky své instance, které jsou před zavoláním readResolve() inicializovány, takže pokud třeba je v enumeraci použito id jako identifikátor instance a getInstance(int) slouží jako metoda k získání instance, pak lze:
Past na turisty: Pokud loadujete pomocí více classloaderů (např. J2EE aplikace), tak se může stát, že budete mít více instancí "singletonu", čili na singletony obecně pozor :-)
Pozor, aktualizace: Ukazuje se, že od verze 1.5 lze použít "volatile" při deklaraci proměnné, která bude držet instanci singletonu, k tomu, aby double-checked locking fungoval, takže proč ne :-)
Eager loading (při prvním použití třídy Singleton se inicializuje INSTANCE):
public class Singleton {
public final static Singleton INSTANCE = new Singleton();
}
Lazy loading (INSTANCE se inicializuje až při volání metody getInstance(), která používá vnitřní třídu):
public class Singleton {
private static class SingletonHolder {
private final static Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
private Singleton() {}
}
V nadpisu je i zmínka o serializaci. Problém je v tom, že při deserializaci singletonu se fakticky nepoužije správná instance singletonu, ale udělá se nová. Toto jde řešit pomocí readResolve() a lze velmi dobře použít u enumerací dělaných pomocí singletonu.
public class Singleton implements Serializable {
public final static Singleton INSTANCE = new Singleton();
private Object readResolve() throws java.io.ObjectStreamException {
return INSTANCE;
}
}
Přičemž readResolve() je normální metoda, která může sahat na položky své instance, které jsou před zavoláním readResolve() inicializovány, takže pokud třeba je v enumeraci použito id jako identifikátor instance a getInstance(int) slouží jako metoda k získání instance, pak lze:
public class Singleton implements Serializable {
private int id;
public Singleton getInstance(int id){
...
}
private Object readResolve() throws java.io.ObjectStreamException {
return getInstance(this.id);
}
}
Past na turisty: Pokud loadujete pomocí více classloaderů (např. J2EE aplikace), tak se může stát, že budete mít více instancí "singletonu", čili na singletony obecně pozor :-)
Pozor, aktualizace: Ukazuje se, že od verze 1.5 lze použít "volatile" při deklaraci proměnné, která bude držet instanci singletonu, k tomu, aby double-checked locking fungoval, takže proč ne :-)
Kategorie: Programování.