Tak, jest to konieczne. Istnieje kilka metod, których można użyć do osiągnięcia bezpieczeństwa wątków przy leniwej inicjalizacji:
Synchronizacja drakońska:
private static YourObject instance;
public static synchronized YourObject getInstance() {
if (instance == null) {
instance = new YourObject();
}
return instance;
}
To rozwiązanie wymaga synchronizacji każdego wątku, podczas gdy w rzeczywistości wystarczy tylko kilka pierwszych.
Synchronizacja podwójnego sprawdzenia :
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;
}
To rozwiązanie zapewnia, że tylko kilka pierwszych wątków, które próbują zdobyć twój singleton, musi przejść przez proces pozyskania blokady.
Inicjalizacja na żądanie :
private static class InstanceHolder {
private static final YourObject instance = new YourObject();
}
public static YourObject getInstance() {
return InstanceHolder.instance;
}
To rozwiązanie korzysta z gwarancji modelu pamięci Java dotyczących inicjowania klas, aby zapewnić bezpieczeństwo wątków. Każdą klasę można załadować tylko raz i będzie ona ładowana tylko wtedy, gdy będzie potrzebna. Oznacza to, że pierwszy raz getInstance
zostanie wywołany, InstanceHolder
zostanie załadowany i instance
utworzony, a ponieważ jest to kontrolowane przez ClassLoader
s, nie jest wymagana dodatkowa synchronizacja.