Singleton Pattern

YeJI Kang·2021년 6월 3일
0

Head First Design Pattern

목록 보기
5/14

싱글톤 패턴(Singleton Pattern)의 용도

스레드 풀, 캐시, 대화상자, 사용자 설정 혹은 레지스트리를 처리하는 객체, 로그 기록용 객체, 디바이스 드라이버 등 객체 중에 하나만 있으면 되는 경우 사용합니다.

고전적인 싱글톤 패턴(Singleton Pattern)

고전적인 싱글톤 패턴 방식은 아래와 같습니다.

생성자는 private 으로 설정하고, 객체를 부를 때에는 따로 static 함수를 사용합니다. 인스턴스가 있을 경우에는 그대로 그 인스턴스를 반환하고, 인스턴스가 없을 경우에는 생성하여 인스턴스를 반환하변 됩니다.

public class Singleton {
    private static Singleton uniqueInstance;

    private Singleton() {}

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

싱글톤 패턴(Singleton Pattern)의 정의

싱글톤 패턴
싱글톤 패턴은 해당 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 그 인스턴스에 접근할 수 있도록 하기 위한 패턴입니다.

  • 반드시 생성은 클래스 자신을 통해 하도록 하여, 다른 어떤 클래스에서도 자신의 인스턴스를 추가로 만들지 못하도록 합니다.
  • 싱글톤은 '게으르게' 생성할 수 있습니다. 클래스의 객체가 자원을 많이 잡아먹는 경우에 유용합니다.

멀티스레딩 문제 해결 방법

간단한 해결 방법

객체를 부르는 getInstance() 함수를 동기화시키면 해결할 수 있습니다.

public class Singleton {
    private static Singleton uniqueInstance;

    private Singleton() {}

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

하지만, 위의 방법은 동기화가 좀 아깝게 느껴질 수 있습니다. 왜냐하면 꼭 필요한 시점이 메소드를 시작할 때 뿐이기 때문입니다. Head First Design Patterns에서는 아래와 같이 해결 방법을 제시합니다.

  1. getInstance() 의 속도가 그리 중요하지 않다면 그냥 둡니다.

  2. 인스턴스를 필요할 때 생성하지 말고, 처음부터 만들어 버립니다.

    public class SingletonNoSynchronized {
        private static final SingletonNoSynchronized uniqueInstance = new SingletonNoSynchronized();
    
        private SingletonNoSynchronized() {
        }
    
        private static SingletonNoSynchronized getInstance() {
            return uniqueInstance;
        }
    }
  3. DCL(Double-checking Locking)을 써서 getInstance() 의 동기화 부분을 줄입니다.

    volatile 을 사용하는 이유는 reference를 읽어보는 것이 좋을 것 같습니다.

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

추가 정보

  • 클래스 로더가 두 개 이상이라면 같은 클래스를 여러 번 로딩할 수도 있습니다. 클래스 로더를 여러 개 사용한다면 싱글턴 패턴 사용을 조심해야 합니다. 클래스 로더를 직접 지정해서 이 문제를 피할 수 있습니다.
  • 책에서는 다루지 않고 있지만 싱글톤 패턴을 구현하기 위해 Lazy Holder 라는 방식이 있으니 참고하시면 좋습니다.
profile
재밌는 것만 하고 싶어 ʕ•ﻌ•ʔ

0개의 댓글