Что произойдет при трех вызовах метода getInstance() у Singleton?

«Что произойдет при трех вызовах метода getInstance() у Singleton?» — вопрос из категории Паттерны, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Все три вызова вернут один и тот же экземпляр объекта, так как Singleton гарантирует единственный экземпляр в рамках класса.

Пример:

public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

// В трех разных местах:
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
Singleton s3 = Singleton.getInstance();

System.out.println(s1 == s2 && s2 == s3); // true

Критические аспекты:

  1. Потокобезопасность: Базовая реализация небезопасна для многопоточности. Возможна инициализация нескольких экземпляров.

  2. Потокобезопасные реализации:

    Double-Checked Locking:

    public class ThreadSafeSingleton {
       private static volatile ThreadSafeSingleton instance;
    
       public static ThreadSafeSingleton getInstance() {
           if (instance == null) {
               synchronized (ThreadSafeSingleton.class) {
                   if (instance == null) {
                       instance = new ThreadSafeSingleton();
                   }
               }
           }
           return instance;
       }
    }

    Enum Singleton (рекомендуется Джошуа Блохом):

    public enum EnumSingleton {
       INSTANCE;
    
       public void doSomething() { /* ... */ }
    }
    // Использование: EnumSingleton.INSTANCE.doSomething();
  3. Initialization-on-demand holder idiom:

    public class LazySingleton {
       private LazySingleton() {}
    
       private static class Holder {
           static final LazySingleton INSTANCE = new LazySingleton();
       }
    
       public static LazySingleton getInstance() {
           return Holder.INSTANCE;
       }
    }