[Design Pattern] 싱글톤 패턴(Singleton Pattern)에 대하여

    싱글톤 패턴(Singleton Pattern)이란?

    싱글톤 패턴은 객체를 딱 하나만 생성하여 생성된 객체를 프로그램 어디에서나 접근하여 사용할 수 있도록 하는 패턴을 말합니다. 

     

    싱글톤
    싱글톤 패턴 구조

    개발을 하다 보면 전역적으로 하나의 객체만을 사용해야 하는 경우가 종종 있습니다. 하지만 특별히 제한을 걸어두지 않는다면 객체들이 여러 개로 복제되는 경우가 생길 수 있겠죠. 싱글톤 패턴을 사용하면 객체 생성을 단 한 번으로 제한하여 객체들이 복제되는 경우를 방지할 수 있습니다. 또한 클래스를 사용하는 여러 곳에서 인스턴스를 계속 생성하여 불필요하게 메모리 낭비를 유발할 수 있다고 판단되는 경우에도 싱글톤 패턴을 사용할 수 있습니다.

     

    싱글톤 패턴의 장점

    1. 메모리 낭비를 방지할 수 있다.

    2. 싱글톤으로 만들어진 클래스와 다른 클래스의 인스턴스들의 데이터 공유가 쉽다.

    3. 인스턴스가 절대적으로 한개만 존재하는 것을 보증하기에 개발 시 실수를 줄일 수 있다.

    4. 싱글톤 객체를 사용하지 않는 경우 인스턴스를 생성하지 않는다.

    5. 싱글톤을 상속시킬 수 있다.

     

    싱글톤 패턴의 단점

    1. 전역변수보다 사용하기가 불편하다.

    2. 싱글톤의 역할이 커질수록 결합도가 높아져 객체 지향 설계 원칙에 어긋날 수 있다.

    3. 멀티쓰레드 환경에서 컨트롤이 어렵다.

    4. 객체의 파괴 시점을 컨트롤하기 어려울 수 있다.

     

     싱글톤 패턴(Singleton) 사용법 

    싱글톤 패턴에서는 생성자를 클래스 자체에서만 접근할 수 있도록 private와 같이 접근 제어자를 통해 제어해주어야 합니다. 만약 생성자를 열어두면 해당 클래스를 다른 부분에서 인스턴스화 시킬 수 있기 때문입니다. 접근 제어자로 막는 것과 더불어 수정이 되지 않도록 막아야 합니다. 만약 인스턴스 생성 이후, 수정이 가능하다면 해당 클래스의 인스턴스를 NULL로 초기화시켜버릴 수 있기 때문입니다.

     

    이른 초기화

    public class Singleton {
    
        private static Singleton instance = new Singleton();
    	
        private Singleton() {} //생성자를 private로
    	
        public static Singleton getInstance() {
            return instance;
        }
    }

    이른 초기화는 클래스가 호출될 때 인스턴스를 생성하는 방법입니다. 다만 인스턴스를 사용하지 않아도 생성하기 때문에 효율성이 떨어집니다.(사용하지 않아도 자리를 잡음) 프로그램 실행 시, 전역에서의 싱글톤 클래스의 생성은 알 수 없으므로 해당 단계에서 해당 싱글톤 클래스와 다른 클래스 또는 함수에서 싱글톤 클래스를 참조하고자 하면 문제가 생길 수 있습니다.

     

    늦은 초기화

    public class Singleton {
    
        private static Singleton instance;
    	
        private Singleton () {} //생성자를 private로
    	
        public static Singleton getInstance() {
            if (instance == null){
                instance = new Singleton();
            }    
            
            return instance;
        }
    }

    늦은 초기화는 인스턴스를 실제로 사용할 시점에 생성하는 방법입니다. 인스턴스를 실제로 생성하지 않으면 생성하지 않기에 이른 초기화보다 효율성이 좋긴 합니다만 두 스레드가 동시에 싱글톤 인스턴스에 접근하고 생성이 안된 것을 확인하여 생성한다면 중복으로 생성할 수 있다는 문제가 있을 수 있습니다.

     

    멀티 스레드 환경에서의 늦은 초기화

    public class Singleton {
    
        private static Singleton instance;
    	
        private Singleton () {} //생성자를 private로
    
        public static Singleton getInstance() {
            if (instance == null) {
                //synchroized를 활용하여 여러 인스턴스를 생성하는 것을 방지
                synchronized (Singleton.class) {
                    if (instance == null)
                        instance = new Singleton();
                    }
                }
            return instance;
        }
    }
    

    멀티 스레드 환경에서 늦은 초기화가 중복으로 생성할 수 있는 문제는 synchronized를 사용하여 여러 쓰레드가 getInstance() 메서드에 동시에 접근하는것을 방지할 수 있습니다. 하지만 수 많은 Thread들이 getInstance() 메서드에 접근하게 되면 정체현상이 일어나 성능 저하를 야기합니다. 이를 해결하기 위하여 위의 예제 처럼 Double-Checked Locking 기법을 사용하여 synchronized 영역을 줄일 수 있습니다. 첫번째 if문에서 인스턴스의 존재 여부를 검사하여 인스턴스가 생성되어 있지 않다면 두 번째 if문에서 다시 한 번 검사할 때 synchronized로 동기화 시키는 방법입니다. 이후에 호출 될때는 인스턴스가 이미 생성되어 있기때문에 synchronized 블록에 접근하지 않습니다.

    댓글

    Designed by JB FACTORY