1. 싱글톤 패턴이란?
소프트웨어 디자인 패턴에서 싱글턴 패턴 (Singleton pattern) 을 따르는 클래스는, 클래스의 인스턴스 개수를 한개만 존재하도록 하는것이다.
시스템 혹은 프로세스에 한개의 객체만 존재해야 좋은 경우들이 있다.
예를들면 DB connection Pool, Thread Pool, Logger, 환경설정을 위한 Config 파일들이 있다.
싱글톤 패턴을 사용했을 때 장점은?
- 불필요한 메모리 누수를 방지한다.
- 공통된 객체를 사용해야 하는 상황에서 특정한 하나의 객체만 사용하게 해 준다.
2. Singleton Pattern 의 구현방식
1. Eager Initialization (Ealry Loading)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
위와같은 구현방식은 Singleton 클래스가 클래스 로더에 의해 로드 될때 Singleton 인스턴스가 생성된다.
싱글톤 패턴을 구현하는 가장 간단한 방법이지만 클라이언트에서 사용하지 않더라도 인스턴스는 항상 생성되며
멀티쓰레드 환경에서 Thread - safe 하지 못하는 것을 알 수 있다.
2. Double-checked Locking Pattern
public class SingletonDCL {
private static volatile SingletonDCL instance;
private SingletonDCL(){}
public static SingletonDCL getInstance(){
if (instance == null){
synchronized (SingletonDCL.class){
if (instance == null)
instance = new SingletonDCL();
}
}
return instance;
}
}
코드를 보면 Eager Initailization 방식과 비교했을때 instance 를 생성하는 메소드와 instance 변수를 선언하는 예약어가 다르다는 것을 알 수 있다.
우선 첫번째 instance 가 null 인지 check 하고 인스턴스가 생성된 경우 빠르게 return 하고, 안에서 하는 null check 는 인스턴스가 생성되지 않은 경우 단 한개의 인스턴스만 생성되도록 보장하기 위함이다.
Thread - safe 함을 보장하기 위하여 개선한 이디엄 으로 얼핏보면 완벽해 보일 수 있는 코드다.
하지만 우리는 volatile 예약어에 대해서 해석해볼 필요가 있는데
A, B 2개의 쓰레드가 있다고 가정하자.
1. A 쓰레드가 instance 를 생성하고 sychronized 블록을 벗어남.
2. B 쓰레드가 sychronized 블록에 들어와서 null 체크를 하는 시점에서
3. A 쓰레드에서 생성한 instance 가 Working Memory (CPU Cache) 에만 존재하고 Main Memory 에는 존재하지 않는경우
4. 즉, 메모리간 (Working Memory <ㅡ> Main Memory) 동기화가 완벽하게 이뤄지지 않은 상태라면
5. B 쓰레드는 인스턴스를 또 생성하게 된다.
voliatle 예약어에 대한 자세한 설명은 아래의 포스팅에서 확인이 가능하다.
3. Lazy (Initialization - on - demand) Holder
public class SingletonLazyHolder {
private SingletonLazyHolder(){}
public static SingletonLazyHolder getInstance(){
return LazyHolder.INSTANCE;
}
private static class LazyHolder{
private static final SingletonLazyHolder INSTANCE = new SingletonLazyHolder();
}
}
JVM 의 static initializer 에 의해서 초기화 되는 private static class 의 특정을 이용한 것으로 sychronized 예약어를 사용하지 않고 Thread - safe 가 보장되는 것을 이용한 것이다.
Lazy Holder 라는 표현은 Singleton 클래스 로딩시 Lazy Holder 클래스를 초기화하지 않고 getInstance() 메소드에서
Lazy Holder 를 참조하는 순간 클래스가 로딩되며 초기화되기 때문에 붙여진 표현이다.
4. Enum
public enum SingletonEnum {
INSTANCE
}
Enum 을 사용한 간단한 싱글톤 구현이다.
Enum 은 인스턴스가 여러개 생기지 않도록 확실하게 보장해주고,
직렬화 / 역직렬화에 대한 처리가 필요없다.
직렬화 / 역직렬화 에대해 추가적인 설명을 덛붙히자면
위에서 사용한 Singleton Pattern 은 직렬화를 위해서 Serializable 를 implements 하면 역직렬화 시 readObject() 메소드에 의해서 새로운 인스턴스가 만들어 지기 때문에 Singleton Pattern 이 파괴된다.
자세한 내용은 아래의 포스팅에서 참고할 수 있다.
3. 결론
Singleton Pattern .. 사실 전공수업에서 static 변수에 대해 처음 배울때, 정보처리기사 공부를 할때 배웠던 기억이 난다.
멘토님이 신입 면접에서 Singleton 구현해 보라고 시켰더니 DCL 을 구현해서 놀랐다고 해서
'어 Singleton 구현방식이 여러가지였나..?' 싶어서 더 찾아봤다.
역시 기본기가 중요하다고 했던가.. 멘토링을 진행하면서 포스팅하고 공부했던 개념들이 모두 등장해서
이전에 봤을땐 솔직히 잘 몰라서 넘겼던 부분들을 이해할수 있었던거 같다.
다시한번 멘토님에게 감사한 마음을 가지게 되었다.