반응형
[헤드퍼스트 디자인패턴] 04. 싱글턴 패턴(Singleton Pattern)
1. 싱글턴 패턴이란?
클래스 인스턴스를 하나만 만들고, 그 인스턴스로의 전역 접근을 제공한다.
2. 고전적인 싱글턴 패턴 구현법
public class Singleton {
private static Singleton uniqueInstance;
// 기타 인스턴스 변수
private Singleton() {}
public static Singleton getInstance() {
if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// 기타 메소드
}
- 생성자는 private 이므로 외부에서 new 키워드를 통해 객체를 생성할 수 없다.
- 객체를 생성하기 위해서는 Singleton.getInstance() 라는 정적 메소드를 통해서만 생성 가능
- 이미 생성된 객체가 있을 경우, 기존 객체를 반환하고 없을 경우 생성 후 반환 한다.
2.1 고전적인 싱글턴 패턴의 문제점
멀티쓰레드일 경우, 여러 싱글톤 객체가 생성 될 수 있다.
3. 동시성 문제 해결 방법
3.1 Synchronized 키워드 이용
public class Singleton {
private static Singleton uniqueInstance;
// 기타 인스턴스 변수
private Singleton() {}
public static synchronized Singleton getInstance() {
if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// 기타 메소드
}
- synchronized : 한번에 하나의 쓰레드만 실행할 수 있기 때문에 동시성 해결이 가능
- 그러나 동기화가 필요한 시점은 첫 인스턴스 생성 시점이기 때문에 인스턴스가 생성된 후에는 동기화를 유지할 필요가 없으나 위의 코드의 경우 모든 순간 마다 동기화를 하게 된다. -> 불필요한 오버헤드 증가로 인한 속도 저하
- getInstance() 메소드가 애플리케이션에 큰 부담을 주지 않는다면 그냥 둬도 좋으나, 병목으로 작용할 경우 개선 필요
3.2 처음부터 Singelton 인스턴스 생성
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {}
public static synchronized Singleton getInstance() {
return uniqueInstance;
}
// 기타 메소드
}
- 클래스가 로딩될 때 JVM에서 Singleton 인스턴스 생성
- synchronized의 속도 저하 문제 해결
- 애플리케이션 시작 시점에 인스턴스가 생성되기 때문에, 사용하지 않을 경우 자원의 낭비가 된다.
3.3 DCL(Double-Checked Loking) 사용
public class Singleton {
private volatile static Singleton uniqueInstance;
// 기타 인스턴스 변수
private Singleton() {}
public static Singleton getInstance() {
if(uniqueInstance == null) {
synchronized (Signleton.class) {
if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
// 기타 메소드
}
- uniqueInstance가 null일 경우만 synchronized 적용
- 객체가 생성된 후에는 synchronized 체크를 하지 않으므로 속도 저하 문제 해결
volatile란?
- Multi Thread 환경에서 변수 값을 Read 할 때 CPU cache가 아닌 Main Memory에서 값을 읽고 쓰는 것
- Mutli Thread 어플리케이션에서는 성능 향상을 위해 CPU cache를 사용하는데, 이때 발생하는 변수값 불일치 문제를 해결할 수 있다.
- cache를 이용하지 않는 만큼 성능에 영향을 줄 수 있으니, 꼭 필요한 경우만 사용할 것
4. static method를 이용한 Singleton 구현 시 발생 문제
4.1 직렬화와 역직렬화 (Serialization & Deserialization)
- 클래스를 역직렬화할 때 새 인스턴스가 생성되어 싱글턴 속성을 위반 -> readResolver 메소드 구현으로 해결 가능
4.2 리플렉션 (Reflectiom)
- 리플렉션을 이용하면 런타임에 private 생성자에 접근하여 새로운 인스턴스를 생성할 수 있다.
5. enum class 사용
public enum SingletonEnum {
INSTANCE;
int value;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
public class EnumDemo {
public static void main(String[] args) {
SingletonEnum singleton = SingletonEnum.INSTANCE;
System.out.println(singleton.getValue());
singleton.setValue(2);
System.out.println(singleton.getValue());
}
}
- enum 인스턴스의 생성은 기본적으로 thread safe
- enum 타입은 기본적으로 직렬화 가능하므로 Serializable 인터페이스를 구현할 필요가 없고, 리플렉션 문제도 발생하지 않는다.
- 인스턴스가 JVM 내에 하나만 존재한다는 것이 100% 보장 되므로, Java에서 싱글톤을 만드는 가장 좋은 방법으로 권장된다.
6. 싱글턴 패턴 정리
- 어떤 클래스에 싱글턴 패턴을 적용하면 그 클래스의 인스턴스가 1개만 있도록 할 수 있다.
- 싱글턴 패턴을 사용하면 하나뿐인 인스턴스를 어디서든지 접근 할 수 있다.
- 멀티 스레드를 사용하는 애플리케이션에서는 속도와 자원 문제를 파악하고 적절한 구현법을 사용해야 한다.
- 자바의 enum을 쓰면 간단하게 싱글턴을 구현할 수 있다.
728x90
반응형
'스터디 > 헤드퍼스트 디자인패턴' 카테고리의 다른 글
[헤드퍼스트 디자인패턴] 06. 커맨드 패턴(Command Pattern) (0) | 2022.07.10 |
---|---|
[헤드퍼스트 디자인패턴] 05. 팩토리 패턴(Factory Pattern) (1) | 2022.07.03 |
[헤드퍼스트 디자인패턴] 03. 데코레이터 패턴(Decorator Pattern) (0) | 2022.06.18 |
[헤드퍼스트 디자인패턴] 02. 옵저버 패턴(Observer Pattern) (0) | 2022.06.12 |
[헤드퍼스트 디자인패턴] 01. 전략패턴 (0) | 2022.06.05 |
댓글