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.
Draconian synchronization
iDouble check synchronization
getInstance () - metoda musi być statyczna!static
, ale może więcej sensu, jeśli były. Zmieniono zgodnie z wnioskiem.r
nie jest potrzebne do poprawności. To tylko optymalizacja, aby uniknąć dostępu do zmiennego pola, ponieważ jest to znacznie droższe niż dostęp do zmiennej lokalnej.Ten wzorzec wykonuje bezpieczną dla wątków leniwą inicjalizację wystąpienia bez jawnej synchronizacji!
Działa, ponieważ używa programu ładującego klasy, aby wykonać całą synchronizację za Ciebie za darmo: klasa
MySingleton.Loader
jest najpierw dostępna wewnątrzgetInstance()
metody, więcLoader
klasa ładuje się, gdygetInstance()
jest wywoływana po raz pierwszy. Ponadto program ładujący klasy gwarantuje, że cała inicjalizacja statyczna jest zakończona, zanim uzyskasz dostęp do klasy - to zapewnia bezpieczeństwo wątków.To jest jak magia.
W rzeczywistości jest bardzo podobny do wzorca wyliczenia w Jhurtado, ale uważam, że wzorzec wyliczenia stanowi nadużycie koncepcji wyliczenia (chociaż działa)
źródło
final
powinien zostać dodany. Gotowe.Jeśli pracujesz w środowisku wielowątkowym w Javie i chcesz zagwarantować, że wszystkie te wątki mają dostęp do jednej instancji klasy, możesz użyć Enum. Będzie to miało dodatkową zaletę, ułatwiając obsługę serializacji.
a następnie po prostu pozwól swoim wątkom używać Twojej instancji, tak jak:
źródło
Tak, musisz dokonać
getInstance()
synchronizacji. Jeśli tak nie jest, może dojść do sytuacji, w której można utworzyć wiele instancji klasy.Rozważ przypadek, w którym masz dwa wątki, które wywołują
getInstance()
w tym samym czasie. Teraz wyobraź sobie, że T1 wykonuje się tuż poinstance == null
sprawdzeniu, a następnie uruchamia się T2. W tym momencie instancja nie jest tworzona ani ustawiana, więc T2 przejdzie kontrolę i utworzy instancję. Teraz wyobraź sobie, że wykonanie przełącza się z powrotem na T1. Teraz singleton jest tworzony, ale T1 już sprawdził! Zacznie ponownie tworzyć obiekt! ZrobieniegetInstance()
zsynchronizowane Zapobiega to problem.Istnieje kilka sposobów na zapewnienie bezpieczeństwa wątków singletonów, ale
getInstance()
synchronizacja jest prawdopodobnie najprostsza.źródło
Enum singleton
Najprostszym sposobem zaimplementowania Singletona, który jest bezpieczny dla wątków, jest użycie Enum
Ten kod działa od wprowadzenia Enum w Javie 1.5
Podwójnie sprawdzone ryglowanie
Jeśli chcesz zakodować „klasyczny” singleton, który działa w środowisku wielowątkowym (począwszy od Javy 1.5), powinieneś użyć tego.
To nie jest bezpieczne dla wątków przed wersją 1.5, ponieważ implementacja słowa kluczowego volatile była inna.
Wczesne ładowanie Singletona (działa nawet przed Java 1.5)
Ta implementacja tworzy wystąpienie singletona, gdy klasa jest ładowana i zapewnia bezpieczeństwo wątków.
źródło
Możesz również użyć bloku kodu statycznego, aby utworzyć wystąpienie wystąpienia podczas ładowania klasy i zapobiec problemom z synchronizacją wątków.
źródło
instance
ostatnią 2. Należy dokonaćgetInstance()
statycznego.Zapoznaj się z tym postem, aby uzyskać najlepszy sposób implementacji Singletona.
Jaki jest skuteczny sposób implementacji wzorca singleton w Javie?
Zależy to od sposobu, w jaki zaimplementowałeś metodę. Jeśli używasz podwójnego blokowania bez zmiennej lotnej, możesz otrzymać częściowo skonstruowany obiekt Singleton.
Więcej informacji można znaleźć w tym pytaniu:
Dlaczego w tym przykładzie podwójnie sprawdzanego ryglowania użyto dlaczego jest zmienne
Nie jest wymagane, jeśli implementujesz Singleton w poniższe sposoby
Odnieś się do tego pytania, aby uzyskać więcej informacji
Wzorzec projektowy Java Singleton: pytania
źródło
Źródło: Efektywna Java -> Punkt 2
Sugeruje użycie go, jeśli masz pewność, że klasa zawsze pozostanie singleton.
źródło