Safety Singleton pattern
Exploring the concept of the Singleton pattern and its implementation in Kotlin
September 10, 2024
싱글턴 패턴이란?
- 클래스의 인스턴스가 오직 하나만 존재합니다.
- 이 인스턴스에 대한 전역적인 접근점을 제공합니다.
- 싱글턴은 리소스 공유, 설정 관리, 데이터베이스 연결 등에 자주 사용됩니다.
멀티스레드 환경에서의 문제점
멀티스레드 환경에서 단순한 싱글턴 구현은 문제를 일으킬 수 있습니다. 여러 스레드가 동시에 인스턴스를 생성하려 할 때, 의도치 않게 여러 인스턴스가 생성될 수 있기 때문입니다.
다음은 코틀린에서 멀티스레드 안전한 싱글턴을 구현하는 방법입니다.
class Singleton private constructor() {
companion object {
@Volatile
private var instance: Singleton? = null
fun createInstance() = instance ?: synchronized(this) {
instance ?: Singleton().also { instance = it }
}
}
}
- private 생성자: private constructor()를 사용하여 외부에서 직접 인스턴스를 생성하는 것을 방지합니다.
- companion object: 코틀린에서 정적 멤버를 구현하기 위해 사용됩니다.
- @Volatile: 이 어노테이션은 instance 변수가 모든 스레드에서 항상 최신 값을 읽도록 보장합니다.
- Double-Checked Locking: createInstance() 메서드에서 사용된 이 패턴은 성능을 최적화하면서도 스레드 안전성을 보장합니다.
구현 설명
- 먼저 instance가 null인지 확인합니다. (instance ?:)
- null이 아니면 바로 그 인스턴스를 반환합니다.
- null이면 synchronized 블록에 진입합니다.
- 동기화 블록 내에서 다시 한 번 null 체크를 합니다. (Double-Checked Locking)
- 여전히 null이면 새 인스턴스를 생성하고 instance에 할당합니다.
이 방식은 첫 번째 null 체크로 대부분의 경우 동기화 오버헤드를 피하면서, 동시에 여러 스레드가 인스턴스를 생성하는 것을 방지합니다.
fun main() {
val singleton1 = Singleton.createInstance()
val singleton2 = Singleton.createInstance()
println(singleton1 === singleton2) // true 출력
singleton1.print() // "Singleton" 출력
}