
"헤드 퍼스트 디자인 패턴(개정판)"을 읽고 정리한 내용입니다.
5. 하나뿐인 특별한 객체 만들기
싱글턴 패턴
싱글턴 패턴(Singleton Pattern)은 클래스 인스턴스가 오직 1개만 있음을 보장하고, 그 인스턴스로의 전역 접근을 제공한다.
멀티스레딩 문제 해결하기
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
위 코드의 경우에는 스레드가 여러개 접근할 경우에 동기화 문제가 발생할 수 있다.
멀티스레딩 해결 방법 1 : 지연 초기화(Lazy Initialization)
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
멀티스레드 환경에서는 위 코드와 같이 synchronized 키워드를 사용해서 한 스레드가 메소드 사용을 끝내기 전까지 블로킹하도록 수정한다. 하지만, 이 방식처럼 메소드 를 동기화하면 성능이 100배 정도 저하된다.
멀티스레딩 해결 방법 2 : 즉시 초기화(Eager Initialization)
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {}
public static synchronized Singleton getInstance() {
return uniqueInstance;
}
}
위 코드처럼 정적 초기화 부분에서 인스턴스를 생성할 경우에는 멀티스레드 환경에서도 문제가 발생하지 않고 편하게 사용할 수 있다. 하지만, 애플리케이션이 시작될 때 인스턴스를 바로 생성하기 때문에 인스턴스가 사용되지 않더라도 메모리를 차지하게 된다.
멀티스레딩 해결 방법 3 : 이중 검사 잠금(DCL, Double-Checked Locking)
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) { // 첫 번째 Check
synchronized (Singleton.class) { // 동기화 블록
if (uniqueInstance == null) { // 두 번째 Check
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
이 방식은 인스턴스가 이미 생성된 경우에 불필요한 동기화를 피하기 위해서 인스턴스가 null 인지 두 번 검사하는 방법이다. 첫 번째 Check 부분에서는 동기화 없이 빠르게 검사하고, 두 번째 Check는 동기화 블록 안에서 검사해서 인스턴스가 아직 생성되지 않은 경우에 객체를 생성한다. 위 코드는 동기화 비용을 최소화하면서도 멀티스레드 환경에서 안전하다.
멀티스레딩 해결 방법 4 : ENUM
enum Singleton {
UNIQUE_INSTANCE;
}
public class SingletonTest {
public static void main(String[] args) {
Singleton singleton = Singleton.UNIQUE_INSTANCE;
}
}
가장 안전하고 간단하게 사용할 수 있는 Singleton 패턴의 구현이다. 자바의 enum은 동기화 문제, 클래스 로딩 문제, 리플렉션, 직렬화와 역직렬화 문제에 대해서 안전을 보장한다.