0Pricing
Java Academy · Lekcja

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

  1. Singleton: implementacje bezpieczne wątkowo
  2. Wzorzec Factory Method
  3. Abstract Factory dla rodzin produktów
  4. Wzorzec Builder z płynnym API
← Powrót do Java Academy