• 클래스의 인스턴스를 하나만 생성하여 사용
  • 셋팅 정보 같이 인스턴스가 여러 개 일경우 문제가 발생하는 경우를 막는다
  • 1개 JVM 당 1개의 인스턴스만 갖음

구현

  • 생성자를 private으로 제한해 외부에서 인스턴스를 생성하지 못하게 한다 (new 금지)
  • 클래스의 인스턴스에 접근할 수 있는 public static 메서드를 만들어 제공한다

1. Eager Initialization (이른 초기화)

  • 프로그램 시작시 정적 바인딩을 통해 메모리에 올리는 방식
  • 사용하지 않을 수도 있는 자원을 생성하여 메모리낭비를 유발
  • 멀티쓰레딩 문제가 발생하지 않는다
public class Settings {
    private static Settings instance = new Settings();

    private Settings() {}

    public static Settings getInstance() {
        return instance
    }
}
public class Main {

    public static void main(String[] args) {
        Settings settings1 = Settings.getInstance();
        Settings settings2 = Settings.getInstance();

        System.out.println(settings1 == settings2)      // true
    }
}

2. Lazy Initialization (늦은 초기화)

  • 인스턴스가 필요한 시점에 동적 바인딩을 통해 인스턴스를 생성하는 방식
  • 멀티쓰레드 환경에서 싱글톤을 보장하기 위해 synchronized 사용
  • 사용 전까지 메모리를 사용하지 않는 장점을 갖는다
  • 단점 : getInstance()가 실행될 때 마다 동기화처리를 하기 때문에 성능저하 유발
public class Settings {
    private static Settings instance;

    private Settings() {}

    public static synchronized Settings getInstance() {
        if (instance == null) {
            instance = new Settings();
        }
        return instance
    }
}
  • Double Checking Locking(DCL, Thread-safe)
    • 성능저하를 유발하지 않기 위해 인스턴스가 생성되지 않은 경우만 동기화 블럭이 실행되도록 설정
public class Settings {
    private volatile static Settings instance;

    private Settings() {}

    public static Settings getInstance() {
        if (instance == null) {
            synchronized (Settings.class) {
                if (instance == null) {
                    instance = new Settings();
                }
            }
        }
        return instance
    }
}
  • volatile 키워드는 멀티쓰레드 환경에서 인스턴스 초기화 하는 과정이 올바르게 진행될 수 있도록 해준다
  • 첫번째 if문에서 인스턴스가 생성되지 않은 경우 동기화블럭에 접근해 한번더 존재 유무를 체크하고 인스턴스를 생성한다
  • 이후 재호출시 인스턴스가 존재하기 때문에 동기화블럭을 접근하지 않는다

문제점

  • 직렬화/역직렬화 : 클래스를 직렬화할 때 새로운 인스턴스가 생성된다
  • 리플렉션 : 리플렉션을 이용하면 런타임에 private 생성자에 접근하여 새로운 인스턴스를 생성된다

3. Enum 사용

  • Enum 클래스는 상수가 public static final이며 생성자가 private이다
  • Enum을 사용하면 위의 동기화, 직렬화, 리플렉션 문제를 모두 해결할 수 있다
public enum Settings {
    INSTANCE;
}

장점

  1. 메모리를 효율적으로 사용할 수 있다
    • 하나의 인스턴스만 사용하기 때문에 여러 인스턴스가 생성되는 것으로부터 메모리 낭비를 방지
  2. 데이터 공유가 쉽다
    • 싱글톤 인스턴스는 전역으로 사용되기 때문에 다른 클래스에서 접근하기 쉬워진다

단점

  1. 역할이 커지면 결합도가 높아지며 OCP를 위배할 수 있다