소프트웨어 스터디/디자인패턴

Singleton 패턴

로 얄 2021. 1. 5. 15:53
반응형

위키피디아에 따르면 싱글턴 패턴(Singleton pattern)을 따르는 클래스는, 생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 리턴한다. 이와 같은 디자인 유형을 싱글턴 패턴이라고 한다. 주로 공통된 객체를 여러개 생성해서 사용하는 DBCP(DataBase Connection Pool)와 같은 상황에서 많이 사용된다.

 

즉 프로그램 전역적으로 단 하나의 객체만 존재하며 사용된다라고 해석이 됩니다.

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

    private Singleton() {

    }
}

코드로 보면 위와 같이 구현될 수 있습니다. 여기서 포인트는 생성자가 private 선언하여 아무렇게나 생성하여 사용하는 것을 막고 static으로 구현된 instance를 사용함으로서 전역적으로 하나의 객체를 유지하여 사용하는 것을 알 수 있습니다. 

문제점

만약 단일 Thread 환경에서 사용하단고 하면, 위와 같은 구현은 아무런 문제가 되지 않습니다. 하지만 Multi-Thread 환경에서는 Thread safe 하지 않습니다. 즉 처음 getInstance()를 멀티스래드 환경에서 호출할 경우 다른 객체를 받을 가능성이 존재한다는 것이지요. 그렇게 되면 각 Thread에서 다른 객체를 핸들링하게되며, 일부 Thread에서는 getInstance() 다시 호출 했을 때 이전과 다른 객체를 받게 됩니다. 이를 해결하기 위해서는 getInstance()에 수정이 필요한데 여기에 여러가지 방법이 존재합니다.

 

해결

방법1

가장 간단한 해결책은 아래와 같습니다.

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

    private Singleton() {

    }
}

getInstance() 를 synchronized로 사용하는 것입니다. 이로서 thread safe 해졌습니다만, 해당 method에 동시에 접근이 불가능해지기 때문에 성능저하가 발생할 수 있습니다.

 

방법2

이런 문젤 해결하기 위해 더블체크를 하는 방법을 사용하기도 합니다.

public class Singleton {
    private static Singleton instance;

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

    private Singleton() {

    }
}

이와 같은 방법은 synchronized를 사용하면서도 이로인한 오버해드를 줄일 수 있습니다.

방법3

Bill Pugh Solution 이라 불리는 방식으로 java 5이전에는 메모리 모델에 많은 문제가 있었고, 위와 같은 방식은 Multi-Thread 환경의 특정 시나리오에서 오류를 일으켰습니다. 그래서 Bill Pugh에 의해 제안된 방식으로 내부 정적 클래스를 사용하는 방식입니다.

public class Singleton {
    private Singleton() {

    }

    private static class SingletonHelper {
        private static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHelper.instance;
    }
}

이와 같이 사용할 경우 Singleton class 가 로드될 때 inner class는 로드되지 않기 때문에 객체가 아직 생성되지 않습니다. inner class는 getInstance()가 불릴때만 생성이 됩니다. 따라서 synchronized를 사용하지 않고도 안전한 singleton의 구현이 가능합니다. 떄문에 가장 널리 사용되는 방식이라고 볼 수 있습니다.

 

Kotlin

요즘 많이 사용되는 kotlin에서는 object 가 기본적으로 지원하기 때문에 object로 선언하여 사용하면 자동적으로 singleton으로 사용이 가능합니다. :)

References

 

반응형

'소프트웨어 스터디 > 디자인패턴' 카테고리의 다른 글

Builder Pattern - 빌더패턴  (0) 2021.01.07
Factory Method Pattern  (0) 2021.01.06