Singleton Pattern (싱글턴 패턴)

Posted by RAY.D
2016. 6. 21. 13:27 Design Pattern
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.




참고 및 출처 ::

 - Head First Design Pattern

 - http://www.gurubee.net/pages/viewpage.action?pageId=1507403



* Singleton Pattern (싱글턴 패턴)

 - 싱글턴 패턴은 해당 클래스의 인스턴스가 하나만 만들어진다.

 - 어디서든지 그 인스턴스에 접근할 수 있도록 한다.

 - 클래스에서 자신의 단 하나 뿐인 인스턴스를 관리하도록 만들면 된다.


싱글턴 패턴은 인스턴스가 하나 뿐인 특별한 객체를 만들 수 있게 해주는 패턴이다. 주로 쓰이는 용도는 아래와 같다.

 - 스레드 풀, 캐시, 대화상자, 사용자 설정, 디바이스 드라이버 등등 객체가 전체 프로그램에서 오직 하나만 생성되어야 하는 경우

그럼 전역 변수로 static 으로 선언해서 사용하면 되지 않을까? 만약 전역 변수로 객체를 생성하면 어플리케이션이 실행 될 때 객체가 생성 될 것이다. 그러나 그 객체가 자원을 많이 차지 한다면 사용도 되기 전에, 괜히 자원만 차지할 것이다.




* 고전적인 싱글턴 패턴 구현법

아래 방식의 코드는 다중 스레드를 사용할 때 각 스레드가 getInstance 메소드를 동시에 호출하면(거의 비슷하게) 두 개의 객체가 생겨버릴지도 모른다. 지금은 간단하지만 나중에 복잡한 인스턴스가 두 개 이상 존재할 경우 어떤 결과를 초래하게 될 지 알 수 없다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Singleton {
 
    // private으로 Sinleton클래스의 유일한 인스턴스를 저장하기 위한 정적 변수를 선언
    private static Singleton uniqueInstance;
 
    // 생성자를 private로 선언했기 때문에 Singleton에서만 클래스를 만들 수 있다.
    private Singleton() {
    }
 
    // 클래스의 인스턴스를 만들어서 리턴해 준다.
    public static Singleton getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
 
}




* 멀티스레딩 문제 해결 방법

getInstance() 를 동기화시키기만 하면 멀티스레딩과 관련된 문제가 간단하게 해결된다. 그러나 동기화를 하게 되면 불필요한 오버헤드가 생기기 때문에 속도 문제가 발생할 수 있다. (대부분 그렇다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton {
    private static Singleton uniqueInstance;
 
    // 기타 인스턴스 변수
    private Singleton() {
    }
 
    // synchronized 키워드만 추가하면 두 스레드가 이 메소드를 동시에 실행시키는 일은 일어나지 않게 된다.
    public static synchronized Singleton getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
    // 기타 메소드
}





* 더 효율적인 방법?

1. getInstance()의 속도가 그리 중요하지 않다면 그냥 내버려 둔다.

 - 메소드를 동기화하면 성능이 100배 정도 저하된다는 것을 기억해 두자.

 - 만약 getInstance() 가 애플리케이션에서 병목으로 작용한다면 다른 방법을 생각해봐야 한다.



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

이런 접근법을 사용하면 클래스가 로딩될 때 JVM에서 Singleton의 유일한 인스턴스를 생성해 준다.

1
2
3
4
5
6
7
8
9
10
public class Singleton {
    private static Singleton uniqueInstance = new Singleton();
 
    private Singleton() {
    }
 
    public static Singleton getInstance() {
        return uniqueInstance;
    }
}



3. DCL(Double-Checking Locking)을 써서 getInstance()에서 동기화되는 부분을 줄인다.

DCL(Double-Checking Locking)을 사용하면, 일단 인스턴스가 생성되어 있는지 확인한 다음, 생성되어 있지 않았을 때만 동기화를 할 수 있다.volatile 키워드를 사용하여 멀티스레딩을 쓰더라도 uniqueInstance 변수가 Singleton 인스턴스로 초기화 되는 과정이 올바르게 할 수 있다. DCL은 자바 1.4 이하 버전에서 사용할 수 없다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Singleton {
    private volatile static Singleton uniqueInstance;
 
    private Singleton() {   }
 
    public static Singleton getInstance() {
        if (uniqueInstance == null) {
            // 이렇게 하면 처음에만 동기화 된다
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}