Singleton: implementacje bezpieczne wątkowo
Zbuduj bezpieczny wątkowo Singleton, używając blokady z podwójnym sprawdzeniem, enuma lub wzorca holder.
Singleton: implementacje bezpieczne wątkowo to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.
Wzorzec Singleton
Singleton zapewnia istnienie tylko jednej instancji klasy w JVM. Typowe zastosowania to menedżery konfiguracji, pule połączeń i usługi logowania.
Naiwny Singleton — brak bezpieczeństwa wątkowego
Klasyczny leniwy Singleton zawodzi przy współbieżnym dostępie: dwa wątki mogą jednocześnie przejść sprawdzenie wartości null i utworzyć dwie instancje.
public class Config {
private static Config instance;
private Config() {}
public static Config getInstance() {
if (instance == null) { // race condition!
instance = new Config();
}
return instance;
}
}Metoda synchronized — prosta, ale wolna
Dodanie synchronized do getInstance() zapewnia bezpieczeństwo wątkowe, ale powoduje blokadę przy każdym wywołaniu, nawet gdy instancja już istnieje — jest to niepotrzebny narzut.
public static synchronized Config getInstance() {
if (instance == null) instance = new Config();
return instance;
}Podwójnie sprawdzane blokowanie
Najpierw należy sprawdzić wartość bez blokady, a następnie sprawdzić ją ponownie wewnątrz bloku synchronized. Pole wymaga oznaczenia jako volatile, aby zapobiec zmianie kolejności instrukcji.
public class Config {
private static volatile Config instance;
private Config() {}
public static Config getInstance() {
if (instance == null) {
synchronized (Config.class) {
if (instance == null) instance = new Config();
}
}
return instance;
}
}Holder inicjalizowany na żądanie
Najbezpieczniejszy i najbardziej wydajny leniwy Singleton: prywatna statyczna klasa zagnieżdżona przechowuje instancję. JVM ładuje ją dopiero przy pierwszym wywołaniu getInstance() — ładowanie klas jest z natury bezpieczne wątkowo.
public class Config {
private Config() {}
private static final class Holder {
static final Config INSTANCE = new Config();
}
public static Config getInstance() {
return Holder.INSTANCE;
}
}Singleton oparty na enum
W książce Effective Java zaleca się użycie enum z jedną stałą. To rozwiązanie jest zwięzłe, bezpieczne wątkowo, odporne na problemy z serializacją i niewrażliwe na ataki za pomocą refleksji.
public enum AppConfig {
INSTANCE;
private String apiUrl = "https://api.example.com";
public String getApiUrl() { return apiUrl; }
}
// Usage:
String url = AppConfig.INSTANCE.getApiUrl();Inicjalizacja eager
Jeśli koszt uruchomienia jest akceptowalny, można zainicjalizować instancję podczas ładowania klasy za pomocą pola static final. Rozwiązanie jest proste i bezpieczne wątkowo, ale instancja zostanie utworzona nawet wtedy, gdy nigdy nie będzie używana.
public class Config {
private static final Config INSTANCE = new Config();
private Config() {}
public static Config getInstance() { return INSTANCE; }
}Singleton i serializacja
Singleton zserializowany, a następnie zdeserializowany tworzy nową instancję, chyba że zostanie dodana metoda readResolve() zwracająca INSTANCE. Podejście oparte na enum obsługuje ten przypadek automatycznie.
private Object readResolve() { return INSTANCE; }Singleton i ataki za pomocą refleksji
Refleksja może wywoływać prywatne konstruktory. Należy się przed tym zabezpieczyć, zgłaszając wyjątek w konstruktorze, jeśli instancja już istnieje. Singletony oparte na enum są odporne na ten problem zgodnie ze specyfikacją JVM.
private Config() {
if (Holder.INSTANCE != null) {
throw new IllegalStateException("Use getInstance()");
}
}Singleton w ramach frameworków wstrzykiwania zależności
W Spring ziarna są domyślnie singletonami (@Scope("singleton")). Zwykle nie trzeba ręcznie implementować wzorca Singleton — należy pozwolić, aby zarządzał nim kontener DI.
@Component // Spring manages a single instance
public class FeatureFlags {
private final boolean darkMode = true;
public boolean isDarkMode() { return darkMode; }
}Testowanie singletonów
Singletony są trudne do testowania jednostkowego, ponieważ przechowują globalny stan. Należy preferować wstrzykiwanie zależności zamiast ręcznego kodu Singleton i wstrzykiwać atrapy instancji w testach za pomocą wstrzykiwania przez konstruktor.
Szybkie sprawdzenie
Jakie podejście do implementacji singletonu jest zalecane w książce Effective Java?
Podsumowanie
W przypadku singletonów bezpiecznych wątkowo należy używać holdera inicjalizowanego na żądanie lub podejścia opartego na enum. Należy unikać naiwnej leniwej inicjalizacji. W aplikacjach Spring warto powierzyć frameworkowi zarządzanie zakresem.
Często zadawane pytania
Czy lekcja „Singleton: implementacje bezpieczne wątkowo” jest bezpłatna?
Tak — pełny tekst „Singleton: implementacje bezpieczne wątkowo” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Singleton: implementacje bezpieczne wątkowo”?
Zbuduj bezpieczny wątkowo Singleton, używając blokady z podwójnym sprawdzeniem, enuma lub wzorca holder. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Java Academy?
Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Singleton: implementacje bezpieczne wątkowo”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?
Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Singleton: implementacje bezpieczne wątkowo
- Wzorzec Factory Method
- Abstract Factory dla rodzin produktów
- Wzorzec Builder z płynnym API