Jaki jest skuteczny sposób na implementację wzorca singleton w Javie?
java
singleton
design-patterns
Riyaz Mohammed Ibrahim
źródło
źródło
Odpowiedzi:
Użyj wyliczenia:
Joshua Bloch wyjaśnił to podejście w swoim przemówieniu na temat efektywnego ponownego ładowania Java na Google I / O 2008: link do wideo . Zobacz także slajdy 30-32 jego prezentacji ( efektywna_java_reloaded.pdf ):
Edit: część online "Effective Java" mówi:
źródło
W zależności od zastosowania istnieje kilka „poprawnych” odpowiedzi.
Od wersji java5 najlepszym sposobem na to jest użycie wyliczenia:
Przed java5 najprostszym przypadkiem jest:
Omówmy kod. Po pierwsze, chcesz, aby klasa była ostateczna. W tym przypadku użyłem
final
słowa kluczowego, aby poinformować użytkowników, że jest ono ostateczne. Następnie musisz ustawić konstruktora jako prywatny, aby uniemożliwić użytkownikom tworzenie własnego Foo. Zgłoszenie wyjątku od konstruktora uniemożliwia użytkownikom użycie refleksji w celu utworzenia drugiego Foo. Następnie tworzyszprivate static final Foo
pole do przechowywania jedynej instancji ipublic static Foo getInstance()
metodę jej zwracania. Specyfikacja Java zapewnia, że konstruktor jest wywoływany tylko wtedy, gdy klasa jest używana po raz pierwszy.Jeśli masz bardzo duży obiekt lub ciężki kod konstrukcyjny ORAZ masz również inne dostępne metody statyczne lub pola, które mogą być użyte przed potrzebą wystąpienia instancji, wtedy i tylko wtedy musisz użyć leniwej inicjalizacji.
Możesz użyć a,
private static class
aby załadować instancję. Kod wyglądałby wtedy następująco:Ponieważ linia
private static final Foo INSTANCE = new Foo();
jest wykonywana tylko wtedy, gdy faktycznie używana jest klasa FooLoader, zajmuje się ona leniwą instancją i gwarantuje, że jest bezpieczna dla wątków.Jeśli chcesz również móc serializować swój obiekt, musisz upewnić się, że deserializacja nie utworzy kopii.
Metoda
readResolve()
upewni się, że zostanie zwrócona jedyna instancja, nawet jeśli obiekt został zserializowany w poprzednim uruchomieniu programu.źródło
Oświadczenie: Właśnie streściłem wszystkie niesamowite odpowiedzi i napisałem to własnymi słowami.
Wdrażając Singleton mamy 2 opcje
1. Leniwe ładowanie
2. Wczesne ładowanie
Leniwe ładowanie dodaje nieco narzutu (szczerze mówiąc), więc używaj go tylko wtedy, gdy masz bardzo duży obiekt lub ciężki kod konstrukcyjny ORAZ masz również inne dostępne metody statyczne lub pola, które mogą być użyte przed potrzebą wystąpienia, wtedy i tylko wtedy musisz użyć leniwej inicjalizacji. W przeciwnym razie dobrym wyborem jest wczesne ładowanie.
Najprostszym sposobem wdrożenia Singleton jest
Wszystko jest dobrze, z wyjątkiem wczesnego singletona. Spróbujmy leniwie ładowanego singletona
Jak dotąd tak dobrze, ale nasz bohater nie przetrwa, walcząc samotnie z wieloma złymi wątkami, które chcą wielu instancji naszego bohatera. Więc chrońmy go przed złym wielowątkowością
ale nie wystarczy chronić bohatera, naprawdę !!! To najlepsze, co możemy / powinniśmy zrobić, aby pomóc naszemu bohaterowi
Nazywa się to „idiomem podwójnie sprawdzanego blokowania”. Łatwo zapomnieć o niestabilnym stwierdzeniu i trudno zrozumieć, dlaczego jest to konieczne.
Aby uzyskać szczegółowe informacje: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
Teraz jesteśmy pewni złej nici, ale co z okrutną serializacją? Musimy się upewnić, że nawet podczas de-serializacji nie powstaje żaden nowy obiekt
Metoda
readResolve()
upewni się, że zostanie zwrócona jedyna instancja, nawet jeśli obiekt został zserializowany w poprzednim uruchomieniu naszego programu.Wreszcie dodaliśmy wystarczającą ochronę przed wątkami i serializacją, ale nasz kod wygląda na nieporęczny i brzydki. Dajmy naszemu bohaterowi przeróbkę
Tak, to nasz bardzo ten sam bohater :)
Ponieważ linia
private static final Foo INSTANCE = new Foo();
jest wykonywana tylko wtedy, gdy klasaFooLoader
jest rzeczywiście używana, to zajmuje się leniwą instancją,i czy gwarantuje to, że jest wątkowo bezpieczny.
Dotarliśmy do tej pory, oto najlepszy sposób, aby osiągnąć wszystko, co zrobiliśmy, to najlepszy możliwy sposób
Które wewnętrznie będą traktowane jak
Otóż to! Nigdy więcej strachu przed serializacją, wątkami i brzydkim kodem. Również singleton ENUMS jest leniwie inicjowany .
-Joshua Bloch w „Skutecznej Javie”
Teraz mogłeś zrozumieć, dlaczego ENUMS są uważane za najlepszy sposób na wdrożenie Singleton i dziękuję za cierpliwość :)
Zaktualizowałem go na moim blogu .
źródło
serialVersionUID
z0L
. Trzeci problem: brak możliwości dostosowania: Wszelkie metody writeObject, readObject, readObjectNoData, writeReplace i readResolve zdefiniowane przez typy wyliczeniowe są ignorowane podczas serializacji i deserializacji.Rozwiązanie opublikowane przez Stu Thompson jest ważne w Javie 5.0 i nowszych. Ale wolałbym go nie używać, ponieważ uważam, że jest on podatny na błędy.
Łatwo zapomnieć o niestabilnym stwierdzeniu i trudno zrozumieć, dlaczego jest to konieczne. Bez niestabilności kod ten nie byłby już bezpieczny dla wątków z powodu podwójnie sprawdzonego blokowania antipattern. Więcej informacji na ten temat można znaleźć w paragrafie 16.2.4 „ Współbieżność Java” . W skrócie: Ten wzorzec (przed Javą 5.0 lub bez zmiennej instrukcji) może zwrócić odwołanie do obiektu Bar, który jest (nadal) w niepoprawnym stanie.
Ten wzór został wymyślony w celu optymalizacji wydajności. Ale to naprawdę nie jest już poważny problem. Poniższy leniwy kod inicjujący jest szybki i - co ważniejsze - łatwiejszy do odczytania.
źródło
getBar()
. (A jeśligetBar
nazywa się to „za wcześnie”, to napotkasz ten sam problem bez względu na to, w jaki sposób implementowane są pojedyncze). Możesz zobaczyć leniwe ładowanie kodu powyżej: pastebin.com/iq2eayiRWątek bezpieczny w Javie 5+:
EDYCJA : Zwróć uwagę na
volatile
modyfikator tutaj. :) Jest to ważne, ponieważ bez niego JMM (Java Memory Model) nie gwarantuje, że inne wątki zobaczą zmiany jego wartości. Synchronizacja nie dba o to - tylko szereguje dostęp do tego bloku kodu.EDYCJA 2 : Odpowiedź @Bno szczegółowo opisuje podejście zalecane przez Billa Pugh (FindBugs) i jest lepsza. Przeczytaj i zagłosuj na jego odpowiedź.
źródło
Zapomnij o leniwej inicjalizacji , to jest zbyt problematyczne. To jest najprostsze rozwiązanie:
źródło
Upewnij się, że naprawdę tego potrzebujesz. Poszukaj w Google „singleton anti-pattern”, aby zobaczyć argumenty przeciwko niemu. Wydaje mi się, że nie ma w tym nic złego, ale jest to tylko mechanizm ujawniania niektórych globalnych zasobów / danych, więc upewnij się, że jest to najlepszy sposób. W szczególności uważam, że wstrzykiwanie zależności jest bardziej przydatne, szczególnie jeśli używasz testów jednostkowych, ponieważ DI pozwala na użycie kpionych zasobów do celów testowych.
źródło
Jestem zaskoczony niektórymi odpowiedziami sugerującymi DI jako alternatywę dla używania singletonów; są to niepowiązane pojęcia. Za pomocą DI można wstrzykiwać instancje singletonowe lub nie singletonowe (np. Na wątek). Przynajmniej jest to prawdą, jeśli używasz Spring 2.x, nie mogę mówić o innych frameworkach DI.
Tak więc moją odpowiedzią na PO byłoby (we wszystkich, z wyjątkiem najbardziej trywialnego przykładowego kodu), aby:
Takie podejście zapewnia niezłą architekturę odsprzężoną (a zatem elastyczną i testowalną), w której użycie singletonu jest łatwo odwracalnym szczegółem implementacji (pod warunkiem, że wszystkie singletony, których używasz, są oczywiście bezpieczne dla wątków).
źródło
TicketNumberer
która musi mieć jedną globalną instancję i gdzie chcesz napisać klasęTicketIssuer
zawierającą wiersz koduint ticketNumber = ticketNumberer.nextTicketNumber();
. W tradycyjnym myśleniu singletonowym poprzednia linia kodu musiałaby być podobnaTicketNumberer ticketNumberer = TicketNumberer.INSTANCE;
. W myśleniu DI klasa miałaby konstruktora podobnego dopublic TicketIssuer(TicketNumberer ticketNumberer) { this.ticketNumberer = ticketNumberer; }
.main
metoda aplikacji (lub jeden z jej stronników) stworzyłaby zależność, a następnie wywołała konstruktor. Zasadniczo użycie zmiennej globalnej (lub metody globalnej) jest po prostu prostą formą wzorca przerażającego lokalizatora usług i można ją zastąpić wstrzykiwaniem zależności, tak jak każde inne użycie tego wzorca.Naprawdę zastanów się, dlaczego potrzebujesz singletonu przed jego napisaniem. Trwa quasi-religijna debata na temat korzystania z nich, na którą można dość łatwo natknąć się, jeśli będziesz wyszukiwać singletony w Javie.
Osobiście staram się unikać singletonów tak często, jak to możliwe, z wielu powodów, z których większość można znaleźć w Google singletonach. Wydaje mi się, że dość często singletony są nadużywane, ponieważ są one łatwe do zrozumienia dla wszystkich, są wykorzystywane jako mechanizm wprowadzania „globalnych” danych do projektu OO i są używane, ponieważ łatwo jest ominąć zarządzanie cyklem życia obiektu (lub naprawdę myśląc o tym, jak możesz zrobić A od środka B). Spójrz na takie rzeczy jak Inversion of Control (IoC) lub Dependency Injection (DI), aby uzyskać ładne pole środkowe.
Jeśli naprawdę go potrzebujesz, to wikipedia ma dobry przykład poprawnej implementacji singletona.
źródło
Poniżej przedstawiono 3 różne podejścia
1) Wylicz
2) Podwójnie zaznaczone Blokowanie / Leniwe ładowanie
3) Metoda statyczna fabryczna
źródło
Używam Spring Framework do zarządzania moimi singletonami. Nie wymusza „singletonowości” klasy (czego tak naprawdę nie można zrobić, jeśli w grę wchodzi wiele modułów ładujących klasy), ale zapewnia naprawdę łatwy sposób budowania i konfigurowania różnych fabryk do tworzenia różnych typów obiektów.
źródło
Wikipedia ma kilka przykładów singletonów, także w Javie. Implementacja Java 5 wygląda całkiem kompletnie i jest bezpieczna dla wątków (zastosowano podwójnie sprawdzone blokowanie).
źródło
Wersja 1:
Leniwe ładowanie, wątek bezpieczny z blokowaniem, niska wydajność z powodu
synchronized
.Wersja 2:
Leniwe ładowanie, bezpieczny wątek z nieblokującą, wysoką wydajnością.
źródło
Jeśli nie potrzebujesz leniwego ładowania, po prostu spróbuj
Jeśli chcesz leniwego ładowania i chcesz, aby Twój Singleton był bezpieczny dla wątków, wypróbuj wzór podwójnego sprawdzania
Ponieważ wzorzec podwójnego sprawdzania nie działa (z powodu problemów z kompilatorami, nie wiem nic więcej na ten temat.), Możesz również spróbować zsynchronizować całą metodę getInstance lub utworzyć rejestr dla wszystkich Singletonów.
źródło
volatile
Powiedziałbym Enum singleton
Singleton używający enum w Javie jest ogólnie sposobem deklarowania enum singleton. Enum singleton może zawierać zmienną instancji i metodę instancji. Dla uproszczenia należy również pamiętać, że jeśli używasz dowolnej metody instancji, musisz zapewnić bezpieczeństwo wątków tej metody, jeśli w ogóle wpływa ona na stan obiektu.
Zastosowanie wyliczenia jest bardzo łatwe do wdrożenia i nie ma żadnych wad związanych z serializowanymi obiektami, które należy obejść na inne sposoby.
Możesz uzyskać do niego dostęp
Singleton.INSTANCE
znacznie łatwiej niż wywoływaniegetInstance()
metody w Singleton.Innym problemem związanym z konwencjonalnymi singletonami jest to, że po wdrożeniu
Serializable
interfejsu nie są już Singletonami, ponieważreadObject()
metoda zawsze zwraca nową instancję, taką jak konstruktor w Javie. Można tego uniknąć, używającreadResolve()
i odrzucając nowo utworzoną instancję, zastępując singletonem, jak poniżejMoże to stać się jeszcze bardziej skomplikowane, jeśli klasa Singleton utrzyma stan, ponieważ musisz sprawić, by stały się przejściowe, ale w Enum Singleton, Serializacja jest gwarantowana przez JVM.
Dobra lektura
źródło
źródło
Może się trochę spóźnić do tej gry, ale istnieje wiele niuansów przy wdrażaniu singletonu. Wzór uchwytu nie może być stosowany w wielu sytuacjach. I IMO, gdy używasz zmiennej - powinieneś również użyć zmiennej lokalnej. Zacznijmy od początku i powtórzmy problem. Zobaczysz o co mi chodzi.
Pierwsza próba może wyglądać mniej więcej tak:
Tutaj mamy klasę MySingleton, która ma prywatny element statyczny o nazwie INSTANCE i publiczną metodę statyczną o nazwie getInstance (). Przy pierwszym wywołaniu getInstance () element INSTANCE ma wartość NULL. Przepływ następnie przejdzie w stan tworzenia i utworzy nową instancję klasy MySingleton. Kolejne wywołania getInstance () stwierdzą, że zmienna INSTANCE jest już ustawiona, a zatem nie utworzą kolejnej instancji MySingleton. Dzięki temu istnieje tylko jedna instancja MySingleton, która jest współużytkowana przez wszystkie osoby wywołujące getInstance ().
Ale ta implementacja ma problem. Aplikacje wielowątkowe będą miały warunek wyścigu podczas tworzenia pojedynczej instancji. Jeśli wiele wątków wykonania trafi do metody getInstance () w tym samym czasie (lub w pobliżu), każdy z nich zobaczy element INSTANCE jako zerowy. Spowoduje to, że każdy wątek utworzy nową instancję MySingleton, a następnie ustawi element członkowski INSTANCE.
W tym celu użyliśmy słowa kluczowego synchronizowanego w sygnaturze metody, aby zsynchronizować metodę getInstance (). To z pewnością naprawi naszą rasę. Wątki będą teraz blokować i wprowadzać metodę pojedynczo. Ale stwarza to również problem z wydajnością. Ta implementacja nie tylko synchronizuje tworzenie pojedynczej instancji, ale także synchronizuje wszystkie wywołania getInstance (), w tym odczyty. Odczyty nie muszą być synchronizowane, ponieważ po prostu zwracają wartość INSTANCJA. Ponieważ odczyty będą stanowić większość naszych wywołań (pamiętaj, że tworzenie wystąpień następuje tylko przy pierwszym wywołaniu), poniesiemy niepotrzebne pogorszenie wydajności, synchronizując całą metodę.
W tym miejscu przenieśliśmy synchronizację z podpisu metody do zsynchronizowanego bloku, który otacza tworzenie instancji MySingleton. Ale czy to rozwiązuje nasz problem? Cóż, nie blokujemy już odczytów, ale zrobiliśmy też krok do tyłu. Wiele wątków trafi do metody getInstance () w tym samym czasie lub mniej więcej w tym samym czasie i wszystkie zobaczą element INSTANCE jako null. Następnie uderzą w zsynchronizowany blok, w którym otrzymamy blokadę i utworzymy instancję. Kiedy ten wątek wyjdzie z bloku, pozostałe wątki będą walczyć o blokadę, a jeden wątek po kolei spadnie przez blok i utworzy nowe wystąpienie naszej klasy. Jesteśmy więc dokładnie tam, gdzie zaczęliśmy.
Tutaj wystawiamy kolejny czek WEWNĄTRZ bloku. Jeśli element INSTANCE został już ustawiony, pomiń inicjalizację. Nazywa się to blokowaniem z podwójną kontrolą.
To rozwiązuje nasz problem wielokrotnego tworzenia wystąpień. Ale po raz kolejny nasze rozwiązanie stanowi kolejne wyzwanie. Inne wątki mogą nie „widzieć”, że członek INSTANCE został zaktualizowany. Wynika to z tego, jak Java optymalizuje operacje pamięci. Wątki kopiują oryginalne wartości zmiennych z pamięci głównej do pamięci podręcznej procesora. Zmiany wartości są następnie zapisywane w pamięci podręcznej i odczytywane z niej. Jest to funkcja Java zaprojektowana w celu optymalizacji wydajności. Ale to stwarza problem dla naszej implementacji singletonu. Drugi wątek - przetwarzany przez inny procesor lub rdzeń przy użyciu innej pamięci podręcznej - nie zobaczy zmian dokonanych przez pierwszy. Spowoduje to, że drugi wątek zobaczy element INSTANCE jako zerowy, co spowoduje utworzenie nowej instancji naszego singletonu.
Rozwiązujemy to za pomocą niestabilnego słowa kluczowego z deklaracji członka INSTANCE. Dzięki temu kompilator zawsze będzie czytał i zapisywał w pamięci głównej, a nie w pamięci podręcznej procesora.
Ale ta prosta zmiana kosztuje. Ponieważ omijamy pamięć podręczną procesora, za każdym razem, gdy będziemy działać na niestabilnym elemencie INSTANCE, osiągniemy spadek wydajności - co robimy 4 razy. Dokładnie sprawdzamy istnienie (1 i 2), ustawiamy wartość (3), a następnie zwracamy wartość (4). Można argumentować, że ta ścieżka jest przypadkiem marginesowym, ponieważ tworzymy instancję tylko podczas pierwszego wywołania metody. Być może uderzenie wydajności w tworzenie jest dopuszczalne. Ale nawet nasz główny przypadek użycia, czyta, będzie działał na elemencie lotnym dwukrotnie. Raz, aby sprawdzić istnienie, i jeszcze raz, aby zwrócić jego wartość.
Ponieważ uderzenie wydajności wynika z działania bezpośrednio na elemencie lotnym, ustawmy zmienną lokalną na wartość zmiennej i działajmy na zmiennej lokalnej. Spowoduje to zmniejszenie liczby operacji na lotności, a tym samym odzyskanie części utraconych wyników. Zauważ, że musimy ponownie ustawić naszą zmienną lokalną, kiedy wchodzimy do bloku synchronicznego. Zapewnia to, że jest na bieżąco z wszelkimi zmianami, które miały miejsce, gdy czekaliśmy na zamek.
Ostatnio napisałem o tym artykuł. Dekonstrukcja Singletona . Możesz znaleźć więcej informacji na temat tych przykładów i przykładowy wzór „posiadacza”. Istnieje również przykład z realnego świata pokazujący podejście do lotności z podwójną kontrolą. Mam nadzieję że to pomoże.
źródło
BearerToken instance
w twoim artykule nie mastatic
? A co to jestresult.hasExpired()
?class MySingleton
- może powinno byćfinal
?BearerToken
Instancja nie jest statyczna, ponieważ jest częściąBearerTokenFactory
-, który jest skonfigurowany z określonym serwerem autoryzacji. Może być wieleBearerTokenFactory
obiektów - każdy z nich ma własną „pamięć podręczną”BearerToken
, którą rozdaje, aż do wygaśnięcia. TahasExpired()
metodaBeraerToken
jest wywoływana wget()
metodzie fabrycznej, aby upewnić się, że nie rozdaje przeterminowanego tokena. Jeśli upłynie termin ważności, na serwerze autoryzacji zostanie zażądany nowy token. Akapit po bloku kodu wyjaśnia to bardziej szczegółowo.Oto jak zaimplementować prosty
singleton
:Oto jak prawidłowo leniwie stworzyć
singleton
:źródło
getInstance()
. Ale tak naprawdę, jeśli nie masz żadnych innych metod statycznych w swojej klasieSingleton
i tylko dzwonisz,getInstance()
nie ma prawdziwej różnicy.Potrzebujesz podwójnego sprawdzania idiomu, jeśli chcesz leniwie ładować zmienną instancji klasy. Jeśli musisz leniwie ładować zmienną statyczną lub singletona, potrzebujesz inicjalizacji na żądanie .
Ponadto, jeśli singleton musi być serilizowalny, wszystkie inne pola muszą być przejściowe i należy zaimplementować metodę readResolve () w celu utrzymania niezmienności obiektu singletonu. W przeciwnym razie, za każdym razem, gdy obiekt zostanie przekształcony z postaci szeregowej, zostanie utworzona nowa instancja obiektu. ReadResolve () zastępuje nowy obiekt odczytany przez readObject (), co zmusiło ten nowy obiekt do wyrzucania elementów bezużytecznych, ponieważ nie odwołuje się do niego żadna zmienna.
źródło
Różne sposoby tworzenia obiektu singleton:
Według Joshua Blocha - Enum byłby najlepszy.
możesz także użyć blokady podwójnej kontroli.
Można stosować nawet wewnętrzną klasę statyczną.
źródło
Enum singleton
Najprostszym sposobem na wdrożenie Singletona, który jest bezpieczny dla wątków, jest użycie Enum
Ten kod działa od czasu wprowadzenia Enum w Javie 1.5
Podwójnie sprawdzone ryglowanie
Jeśli chcesz zakodować „klasyczny” singleton działający w środowisku wielowątkowym (od Java 1.5), powinieneś go użyć.
Nie jest to wątkowo bezpieczne przed 1.5, ponieważ implementacja niestabilnego słowa kluczowego była inna.
Wczesne ładowanie Singleton (działa nawet przed Java 1.5)
Ta implementacja tworzy singletona po załadowaniu klasy i zapewnia bezpieczeństwo wątków.
źródło
W przypadku JSE 5.0 i nowszych zastosuj podejście Enum, w przeciwnym razie użyj statycznego uchwytu singletona ((leniwe podejście do ładowania opisane przez Billa Pugha). Późniejsze rozwiązanie jest również bezpieczne dla wątków, nie wymaga specjalnych konstrukcji językowych (tj. Lotnych lub zsynchronizowanych).
źródło
Innym argumentem często używanym przeciwko Singletonom są problemy z testowalnością. Singletonów nie można łatwo wyśmiewać do celów testowych. Jeśli okaże się to problemem, chciałbym wprowadzić następującą drobną modyfikację:
Dodana
setInstance
metoda umożliwia ustawienie implementacji makiety klasy singleton podczas testowania:Działa to również w przypadku podejść do wczesnej inicjalizacji:
Ma to tę wadę, że udostępnia tę funkcjonalność również normalnej aplikacji. Inni programiści pracujący nad tym kodem mogą ulec pokusie użycia metody „setInstance” w celu zmiany modyfikacji określonej funkcji, a tym samym zmiany zachowania całej aplikacji, dlatego metoda ta powinna zawierać co najmniej dobre ostrzeżenie w javadoc.
Mimo to, w przypadku możliwości testowania makiet (w razie potrzeby), ujawnienie kodu może być akceptowalną ceną do zapłaty.
źródło
najprostsza klasa singleton
źródło
Nadal uważam, że po java 1.5 enum jest najlepszą dostępną implementacją singletonu, ponieważ zapewnia również, że nawet w środowiskach wielowątkowych - tworzona jest tylko jedna instancja.
public enum Singleton{ INSTANCE; }
i gotowe!
źródło
Obejrzyj ten post.
Przykłady wzorców projektowych GoF w podstawowych bibliotekach Java
W sekcji „Singleton” najlepszej odpowiedzi:
Możesz także nauczyć się przykładu Singleton z samych klas rodzimych Java.
źródło
Najlepszy wzorzec singletonu, jaki kiedykolwiek widziałem, wykorzystuje interfejs dostawcy.
Patrz poniżej:
źródło
Czasami proste ”
static Foo foo = new Foo();
” nie wystarczy. Pomyśl tylko o podstawowym wprowadzeniu danych, które chcesz wykonać.Z drugiej strony musiałbyś zsynchronizować dowolną metodę, która tworzy instancję zmiennej singleton jako takiej. Synchronizacja jako taka nie jest zła, ale może prowadzić do problemów z wydajnością lub blokowania (w bardzo bardzo rzadkich sytuacjach na tym przykładzie. Rozwiązanie to
Co się teraz stanie? Klasa jest ładowana za pomocą modułu ładującego klasy. Bezpośrednio po interpretacji klasy z bajtu Array maszyna wirtualna wykonuje blok statyczny {} . to cała tajemnica: blok statyczny jest wywoływany tylko raz, czas załadowania danej klasy (nazwy) danego pakietu przez jeden moduł ładujący klasy.
źródło
Ponieważ dodaliśmy słowo kluczowe Synchronized przed getInstance, uniknęliśmy warunków wyścigu w przypadku, gdy dwa wątki wywołują getInstance w tym samym czasie.
źródło