이전 포스팅에서는 Volatile과 Synchronize를 정리하면서 스레드도 안전하게 싱글톤 패턴을 구현하는 간단한 예시도 들었다. 다만 아쉽게 느껴진 점은 Volatile을 사용해서 캐시 메모리가 아닌 메인 메모리에 변수를 저장하다보니, 해당 변수에 접근 하면서 발생하는 비용이 꽤나 발생한다는 점이었다. 그래서 그에 대한 해결 방법을 찾아보다가 Bill Pugh's Solution(이하 빌푸의 솔루션)을 찾게 되었다.
빌푸의 솔루션
빌푸의 솔루션은 inner static class를 이용하여, 자바 혹은 코틀린에서 Volatile annotation + Synchronize 조합을 사용하지 않고 안전한 스레드 안에서 싱글톤 패턴을 구현하는 방법이다. 먼저 그 예시를 보면 아래와 같다.
// Volatile + Synchronize 조합 사용
class HelperClass {
companion object {
@Volatile
private var instance: HelperClass? = null
fun getInstance() =
instance ?: synchronized(HelperClass::class.java) {
instance ?: HelperClass().also {
instance = it
}
}
}
}
// 빌푸의 솔루션 - Java
public class HelperClass {
private HelperClass(){}
private static class HelperClassHelper{
private static final HelperClass INSTANCE = new HelperClass();
}
public static HelperClass getInstance(){
return HelperClassHelper.INSTANCE;
}
}
/*
빌푸의 솔루션 - Kotlin
출처 : 냉동코더의 기술 블로그, https://cliearl.github.io/posts/android/understanding-singleton-pattern/
*/
class HelperClass private constructor() {
companion object {
val instance: HelperClass by lazy { Holder.INSTANCE }
}
private object Holder {
val instance = HelperClass()
}
}
Java는 Static을 이용하여 class와 INSTANCE를 메모리에 고정적으로 할당해줬으며, Kotlin에서는 동반객체를 사용하여 instance를 생성해줬으며 lazy로 상수를 선언함으로써 호출이 되기 전까지는 초기화가 진행되지 않게 해줬다.
Java에서는 고정적 할당으로 2번 초기화 되는걸 막아줬으며, Kotlin에서는 lazy를 사용하여 호출되는 시점에 초기화 되게 하여 2번 초기화 되는 것을 막아줬다. 또한 Volatile annotation이 없으므로, 메인 메모리에 직접적으로 접근하지 않아도 되기 때문에 효율성 또한 늘어났다.
'안드로이드 > 알아두기' 카테고리의 다른 글
[안드로이드] 동기화에 대하여 알아보자(2) - Synchronize, @Volatile (1) | 2023.10.24 |
---|---|
[안드로이드] 동기화에 대하여 알아보자(1) - 세마포어와 뮤텍스 (2) | 2023.10.23 |
[안드로이드] Kotlin에서 List, Set, Map에 대하여 (0) | 2023.10.16 |
[안드로이드] SharedFlow와 StateFlow에 대하여 간단하게 알아보자 (0) | 2023.10.14 |
[안드로이드] Cold Stream, Hot Stream에 대하여 (0) | 2023.10.13 |