본문 바로가기
개발

Singleton Pattern

by just다해 2024. 9. 23.

Singleton Pattern 이란?

애플리케이션이 시작될 때 어떤 클래스가 최초 한 번만 메모리를 할당하고(static) 그 메모리에 인스턴스를 만들어 사용하는 디자인 패턴입니다. 생성자가 여러 번 호출되더라도 실제로 생성되는 객체는 하나이며, 최초 생성 이후 호출된 생성자는 최초에 생성한 객체를 반환합니다.

즉, 인스턴스를 하나만 만들어 사용하기 위한 패턴입니다.
Singleton Pattern은 하나의 인스턴스만을 생성하는 책임이 있으며, getInstance() 메서드를 통해 모든 클라이언트에게 동일한 인스턴스를 반환하는 작업을 수행합니다.

public class Singleton {
    private static Singleton singletonObj;

    private Singleton() {}

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

하나의 인스턴스만을 유지하기 위해 인스턴스 생성에 특별한 제약을 걸어둬야 합니다. 새로운 객체를 생성할 수 없도록 생성자에 private 접근 제어자를 지정하고, 유일한 단일 객체를 반환할 수 있도록 정적 메소드를 지원해야 합니다. 또한 유일한 단일 객체를 참조할 정적 참조변수가 필요합니다.

Singleton Pattern 문제점

멀티스레딩 환경에서 Singleton Pattern을 적용하다 보면 문제가 발생할 수 있습니다. 하나만 생성되어야 하는 인스턴스가 여러 스레드에서 동시에 접근하다가 여러 개가 생성될 수 있습니다. 이러한 문제는 아래 두 가지 방법으로 해결할 수 있습니다.

  1. 인스턴스를 만드는 메서드에 동기화하는 방법 (Thread-Safe Initialization)
  2. 정적 변수에 인스턴스를 만들어 바로 초기화하는 방법 (Eager Initialization)

1. Thread-Safe Initialization

public class Singleton {
    private static Singleton singletonObject;

    private Singleton() {}

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

위와 같이 단순히 synchronized 키워드를 사용하면 성능상 이슈가 있을 수 있습니다.

public class Singleton {
    private static volatile Singleton singletonObject;

    private Singleton() {}

    public static Singleton getInstance() {
        if (singletonObject == null) {
            synchronized (Singleton.class) {
                if(singletonObject == null) {
                    singletonObject = new Singleton();
                }
            }
        }
        return singletonObject;
    }
}

다음과 같이 DCL(Double Checking Locking)을 써서 getInstance()에서 동기화 되는 영역을 줄일 수 있습니다. 초기에 객체를 생성하지 않으면서도 동기화하는 범위를 작게 만들었습니다. 하지만 이렇게 구현하면, 멀티코어 환경에서 동작할 때, 하나의 CPU를 제외하고는 다른 CPU 가 lock 이 걸리게 되는 문제점이 발생합니다.

2. Eager Initialization

해당 문제점은 다음과 같이 정적변수에 인스턴스를 만들어 바로 초기화 하는 방법(Eager initialization)으로 해결할 수 있습니다.

public class Singleton {
    private static volatile Singleton singletonObject = new Singleton();

    private Singleton() {}

    public static Singleton getSingletonObject() {
        return singletonObject;
    }
}

왜 Singleton Pattern을 사용할까?

  1. 고정된 메모리 영역을 사용하면서 메모리 낭비를 방지 가능
  2. 전역 인스턴스이기 때문에 다른 클래스의 인스턴스들이 데이터를 공유하기 쉬움
    • (Connection Pool, Thread Pool, 디바이스 설정 객체처럼 공통된 객체를 여러 개 생성해 사용해야 하는 상황에서 많이 사용)
  3. 두 번째 이용 시부터는 객체 로딩 시간이 줄어 성능 향상

'개발' 카테고리의 다른 글

MariaDB connector 2.x 분석  (2) 2024.09.25
DDD - Aggregate  (2) 2024.09.22
DIP 의존 역전 원칙  (0) 2024.09.21
DDD - 도메인 모델링  (0) 2024.09.20
DDD - 도메인이란?  (1) 2024.09.19