ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [디자인패턴]싱글턴 패턴(Singleton Pattern)
    IT, 프로그래밍/Design Patterns 2017. 12. 25. 01:14

    thum


    싱글턴 패턴(Singleton Pattern) = 해당 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 그 인스턴스에 접근할 수 있도록 하기 위한 패턴. 


    싱글턴 객체는 유일하게 존재 하는 객체입니다. 예를 들면, 레지스트리 설정이라던가, DB 커넥션 객체 등이 여러 개 존재하면 심각한 오류가 발생하거나, 자원이 낭비 될 가능성이 있죠. 


    (예를 들어서 2개의 설정 객체의 인스턴스를 무작위로 참조 하게 된다면.. )


    이런 객체들은 단 하나만 존재하게 해서, 그 객체의 인스턴스만 어디서든 쓸 수 있게 만들어 줘야 합니다. 


    싱글턴 객체가 이 싱글턴 객체를 어떻게 만들까요?


    싱글턴 객체를 만드려면 두 가지의 조건을 만족해야 합니다.


    1. 어디서든 접근이 가능하고, 인스턴스를 얻어서 사용할 수 있게 할 것.

    2. 객체는 단 하나만이 메모리에 할당 되어 있을 것.



    이런 조건들을 살펴 보았을때, 인스턴스를 얻는 getInstance()라는 정적 메소드를 정의 하는 것이 좋아 보입니다. 그래서 이렇게 코드를 작성할 수 있죠.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class Singleton{
        
        private static Singleton uniqueInstance; // 유일한 변수를 사용하기 위해 정적 변수로 선언
        
        private Singleton() {}
        
        public static Singleton getInstance() {
            
            if(uniqueInstance == null) {
                
                uniqueInstance = new Singleton();
            } //필요할때만 객체를 생성하는 방식. 게으른 인스턴스 생성(lazy instantiation)
            
            return uniqueInstance;
            
        }
        
     
    }
    cs



    이렇게 되면 변수와 생성자는 private으로 선언되어 있기 때문에 아무도 건드릴 수 없고, 오직 getInstance() 메소드를 통해 이 객체에 접근할 수 있습니다.


    이렇게 작성 된 싱글턴 패턴은 단일 스레드 환경에서는 별 문제가 없지만, 다중 스레드 환경에서는 심각한 문제를 초래할 수 있습니다. 


    스레드는 프로세스와 같이 스케줄링에 의한 문맥교환(context switching)이 발생합니다. 이렇게 되면 2개의 객체가 생성될 수 있습니다. 


    이 문제를 해결하려면 세 가지의 방법이 있습니다.


    1. synchronized , 동기화를 사용한다. 

    2. 인스턴스를 필요할 때 생성하지 말고, 아예 처음부터 정적 객체로 생성한다. 

    3. DCL (Double-Checking Locking)을 사용한다. 



    1. 동기화를 이용한 방법


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class Singleton{
        
        private static Singleton uniqueInstance; // 유일한 변수를 사용하기 위해 정적 변수로 선언
        
        private Singleton() {}
        
        public static synchronized Singleton getInstance() { // 동기화 처리
            
            if(uniqueInstance == null) {
                
                uniqueInstance = new Singleton();
            } //필요할때만 객체를 생성하는 방식. 게으른 인스턴스 생성(lazy instantiation)
            
            return uniqueInstance;
            
        }
        
     
    }
    cs


    --> 이 방법을 사용하면 간단하지만, 객체가 한 번 생성되면 더이상 생성 시킬 필요가 없는데도 불구하고 계속 동기화 처리를 하여 성능 저하가 일어난다는 단점이 있습니다. 약 100배의 속도 저하가 일어납니다.



    2. 아예 처음부터 생성하는 방법


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class Singleton{
        
        private static Singleton uniqueInstance = new Singleton(); // 생성과 동시에 초기화
        
        private Singleton() {}
        
        public static Singleton getInstance() {
            
            return uniqueInstance;
            
        }
        
     
    }
     
    cs


    --> 이렇게 되면 스레드의 영향을 받지 않고 유일한 객체를 생성할 수 있게 됩니다.


    3. DCL 이용하는 방법


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class Singleton {
     
        private volatile Singleton uniqueInstance; 
     
        private Singleton() {
        }
     
        public static Singleton getInstance() {
     
            if (uniqueInstance == null) {
                synchronized (Singleton.class) {
                    if (uniqueInstance == null) {
                        uniqueInstance = new Singleton();
                    }
                }
            }
            return uniqueInstance;
        }
     
    }
    cs



    volatile 예약어에 대한 설명 <-- 보러가기


    --> 이 방식을 사용하면 synchronized만 사용할때 보다 오버헤드를 매우 낮출 수 있으나 자바 1.4 이하 버전에서는 volatile 예약어를 지원하지 않습니다.

Designed by Tistory.