일반적으로 하나의 인스턴스만 존재해야 할 경우 Singleton 패턴을 사용하게 된다. 물론 Single Thread
에서 사용되는 경우에는 문제가 되지 않지만 Multi Thread
환경에서 Singleton 객체에 접근 시 초기화 관련하여 문제가 있다.
보통 Singleton 객체를 얻는 static 메서드는 getInstance()
로 작명하는 게 일반적이다.
그렇다면 어떻게 코드를 작성해야 Singleton 객체를 생성하는 로직을 thread-safe
하게 적용할 수 있을까? 정말 단순하게 별로 신경 쓰고 싶지 않다면 메서드에 synchronized
키워드만 추가해도 무방할 것이다.
그렇지만 좋은 개발자가 되기 위해선 효율적인 코드에 대해서 고민을 해 봐야 한다. 메서드에 Singleton 클래스의 getInstance()
메서드에 synchronized
키워드를 추가하는 건 역할에 비해서 동기화 오버헤드가 심하다고 생각한다.
그래서 개발자들 사이에서 Singleton 초기화 관련하여 많은 이디엄들이 연구되었고 몇몇 이디엄들을 소개하려고 한다.
Double Checked Locking
일명 DCL이라고 불리는 이 기법은 현재 broken 이디엄이고 사용을 권고하지 않지만 이러한 기법이 있었다는 걸 설명하고 싶다. 코드는 다음과 같다.
메서드에 synchronized
를 빼면서 동기화 오버헤드를 줄여보고자 하는 의도로 설계된 이디엄이다. instance
가 null
인지 체크하고 null
일 경우 동기화 블럭에 진입하게 된다. 그래서 최초 객체가 생성된 이후로는 동기화 블럭에 진입하지 않기 때문에 효율적인 이디엄이라고 생각할 수 있다. 하지만 아주 안 좋은 케이스로 정상 동작하지 않을 수 있다. 그래서 broken 이디엄이라고 하는 것이다.
Thread A
와 Thread B
가 있다고 하자. Thread A
가 instance
의 생성을 완료하기 전에 메모리 공간에 할당이 가능하기 때문에 Thread B
가 할당된 것을 보고 instance
를 사용하려고 하나 생성과정이 모두 끝난 상태가 아니기 때문에 오동작할 수 있다는 것이다. 물론 이러할 확률은 적겠지만 혹시 모를 문제를 생각하여 쓰지 않는 것이 좋다.
Enum
Java에 지대한 공헌을 한 Joshua Bloch가 언급한 이디엄이다. 그냥 간단하게 class
가 아닌 enum
으로 정의하는 것이다.
Enum
은 인스턴스가 여러 개 생기지 않도록 확실하게 보장해주고 복잡한 직렬화나 리플렉션 상황에서도 직렬화가 자동으로 지원된다는 이점이 있다. 하지만 Enum
에도 한계는 있다. 보통 Android 개발을 하게 될 경우 보통 Singleton의 초기화 과정에 Context
라는 의존성이 끼어들 가능성이 높다. Enum
의 초기화는 컴파일 타임에 결정이 되므로 매번 메서드 등을 호출할 때 Context
정보를 넘겨야 하는 비효율적인 상황이 발생할 수 있다. 결론은 Enum
은 효율적인 이디엄이지만 상황에 따라 사용이 어려울 수도 있다는 점이다.
LazyHolder
현재까지 가장 완벽하다고 평가받고 있는 이디엄이다. 이 이디엄은 synchronized
도 필요 없고 Java 버전도 상관없고 성능도 뛰어나다. 일단 코드를 보면 아래와 같다.
이 이디엄에 대해서 설명을 하자면 객체가 필요할 때로 초기화를 미루는 것이다. Lazy Initialization
이라고도 한다. Singleton
클래스에는 LazyHolder
클래스의 변수가 없기 때문에 Singleton
클래스 로딩 시 LazyHolder
클래스를 초기화하지 않는다. LazyHolder
클래스는 Singleton
클래스의 getInstance()
메서드에서 LazyHolder.INSTANCE
를 참조하는 순간Class
가 로딩되며 초기화가 진행된다. Class를 로딩하고 초기화하는 시점은 thread-safe
를 보장하기 때문에 volatile
이나 synchronized
같은 키워드가 없어도 thread-safe
하면서 성능도 보장하는 아주 훌륭한 이디엄이라고 할 수 있다.
현재까지 LazyHolder
에 대해서 문제점은 나타나지 않고 있다. 혹시나 Multi Thread 환경에서 Singleton을 고려하고 있다면 LazyHolder
기법을 이용하자.
'Tech Interview > Android' 카테고리의 다른 글
[Android] RecyclerView에 대해 설명하시오. (0) | 2018.11.27 |
---|---|
[Android] 안드로이드 context에 대해 설명하시오 (0) | 2018.11.24 |
[Android] 안드로이드 Task, flag에 대해 설명하시오 (0) | 2018.11.24 |
[Android] Process와 Task에 대해 설명하시오. (0) | 2018.11.24 |
[Android] 안드로이드 브로드캐스트리시버(BroadCastReceiver)에 대해 설명하시오. (0) | 2018.11.22 |