스레드 풀, 캐시, 대화상자, 사용자 설정 혹은 레지스트리를 처리하는 객체, 로그 기록용 객체, 디바이스 드라이버 등 객체 중에 하나만 있으면 되는 경우 사용합니다.
고전적인 싱글톤 패턴 방식은 아래와 같습니다.
생성자는 private
으로 설정하고, 객체를 부를 때에는 따로 static
함수를 사용합니다. 인스턴스가 있을 경우에는 그대로 그 인스턴스를 반환하고, 인스턴스가 없을 경우에는 생성하여 인스턴스를 반환하변 됩니다.
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
싱글톤 패턴
싱글톤 패턴은 해당 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 그 인스턴스에 접근할 수 있도록 하기 위한 패턴입니다.
객체를 부르는 getInstance()
함수를 동기화시키면 해결할 수 있습니다.
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
하지만, 위의 방법은 동기화가 좀 아깝게 느껴질 수 있습니다. 왜냐하면 꼭 필요한 시점이 메소드를 시작할 때 뿐이기 때문입니다. Head First Design Patterns에서는 아래와 같이 해결 방법을 제시합니다.
getInstance()
의 속도가 그리 중요하지 않다면 그냥 둡니다.
인스턴스를 필요할 때 생성하지 말고, 처음부터 만들어 버립니다.
public class SingletonNoSynchronized {
private static final SingletonNoSynchronized uniqueInstance = new SingletonNoSynchronized();
private SingletonNoSynchronized() {
}
private static SingletonNoSynchronized getInstance() {
return uniqueInstance;
}
}
DCL(Double-checking Locking)을 써서 getInstance()
의 동기화 부분을 줄입니다.
volatile
을 사용하는 이유는 reference를 읽어보는 것이 좋을 것 같습니다.
public class DoubleCheckLock {
private volatile static DoubleCheckLock uniqueInstance;
private DoubleCheckLock() {}
public static DoubleCheckLock getInstance() {
if (uniqueInstance == null) {
synchronized (DoubleCheckLock.class) {
if (uniqueInstance == null) {
return new DoubleCheckLock();
}
}
}
return uniqueInstance;
}
}
Lazy Holder
라는 방식이 있으니 참고하시면 좋습니다.