티스토리 뷰

반응형

 객체 중에는 시스템 전체에 딱 하나만 존재해야 유리한 것들이 있다. 대표적으로 스레드 풀이나 캐시, 각종 설정정보 등이 있다. 이들의 인스턴스가 두 개 이상 존재하게 되면 시스템에 좋지 않은 영향을 끼친다. (작게는 자원낭비, 크게는 프로그램 오류)



 객체(인스턴스)를 시스템 전체에 딱 하나 존재하도록 처리하기 위해 전역변수를 사용해도 되지만, 전역변수는 원하는 때에 생성할 수 없다는 단점이 있다. 그래서 사용하는 것이 싱글턴패턴(Singleton Pattern)이다. 싱글턴 패턴을 어떻게 구현하는지 지금부터 '자세히' 알아보자.


 먼저, 싱글턴패턴은 시스템 전체에 딱 하나만 존재하게 하기 위해 생성자를 아무나 접근할 수 없게 막고, 스스로 인스턴스를 관리한다. 아래 코드를 보면 명확해 질 것이다.


public class Singleton {

    // 유일한 인스턴스를 담고있는 static 변수
    private static Singleton singleton;

    // 생성자를 private으로 선언함
    private Singleton(){

    }
    
    // Singleton 인스턴스를 얻기 위한 static 메소드
    public static Singleton getInstance() {

        // 아직 인스턴스가 생성된적이 없음 = 존재하지 않음
        if(singleton == null) {
            
            // 인스턴스 생성. 생성자가 private이니까 클래스 내부에서는 가능.
            singleton = new Singleton();
        }
        return singleton;
    }
}

 주석을 함께 달았으니 참고하면 이해에 도움이 될 것 같다. 싱글턴화 하고 싶은 클래스 내부에 static변수를 선언하고, 이를 이용하여 시스템 전체에 유일하도록 설정했다. 이제 예제의 Singleton 클래스의 인스턴스를 구할 때는 아래와 같이 하면 된다.


Singleton singleton = Singleton.getInstance();




  그런데 이 방법은 완벽한 방법이 아니다. Singleton이라는 클래스의 인스턴스를 얻기 위해, 2개 이상의 스레드가 경합을 벌이는 상황을 가정해보자.





 위 그림을 보면, 스레드 두개가 동시에 Singleton 클래스의 getInstance() 메소드에 진입한 모양이다. 만약 첫번째 스레드에서 (obj == null)인 것을 확인하고, 인스턴스를 생성하기 전에 두번째 스레드가 (obj == null)을 체크한다면? 인스턴스가 두개 생성된다.


 결국, Singleton을 Thread-Safe하도록 만드는 것이 중요한데, 3가지 방법이 있다.



1. Synchronize 키워드 활용.

 동기화처리 시, 성능 문제가 없을 경우 가장 확실한 방법이다. (LOCK을 걸어버림) getInstance() 메소드에 Synchronize 키워드를 추가하여, 동시성을 제어한다. 해당 메소드에는 단 하나의 스레드만 접근 할 수 있게 되어, Thread-Safe하다.



2. 인스턴스를 처음부터 만듦.

 싱글턴 인스턴스를 저장하는 static변수에 바로 인스턴스를 생성해버린다. 이렇게 하면 클래스가 로딩될 때 인스턴스가 생성되므로, Thread-Safe하다.



3. DCL(Double Checked Locking) 을 쓴다

  인스턴스가 생성되었는지만 확인하고, 생성되지 않았을 때만 동기화를 할 수 있다. 1번의 단점이 항상 LOCK이 걸리는 것이었는데, 이를 말끔하게 해결해준다. 아래 소스코드를 참조하자.


public class Singleton {

    // 유일한 인스턴스를 담고있는 static 변수
    private static Singleton singleton;

    // 생성자를 private으로 선언함
    private Singleton(){

    }

    // Singleton 인스턴스를 얻기 위한 static 메소드
    public static Singleton getInstance() {

        if(singleton == null) {
            // 인스턴스가 생성된적이 없을 때 동기화 블록 입장.
            synchronized (Singleton.class) {
                // 다시한번 null check 하여 확실히 한다.
                if(singleton == null) {

                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}


위 소스코드를 요약하면 동기화 블록에 들어가기 전 한번 더 null 검증을 한다.(Double-Checking) 이렇게 구현하면 1번 방법에서 발생하는 오버헤드를 줄일 수 있다.






-끝-






출처 및 참고

http://gampol.tistory.com/entry/Double-checked-locking



«   2022/01   »
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
글 보관함
Total
770,926
Today
360
Yesterday
315