책너두 (헤드 퍼스트 디자인 패턴) 15일차 (~216p)
- Book/헤드 퍼스트 디자인 패턴
- 2023. 7. 13.
요약
- 싱글턴 패턴
- 고전적인 싱글턴 패턴 구현법
- 초콜릿 보일러 코드 살펴보기
- 싱글턴 패턴의 정의
- 초콜릿 보일러에 문제 발생
- 멀티스레딩 문제 살펴보기
- 멀티스레딩 문제 해결하기
- 더 효율적으로 멀티스레딩 문제 해결하기
- getInstance()의 속도가 그리 중요하지 않다면 그냥 둔다.
- 인스턴스가 필요할 때는 생성하지 말고 처음부터 만든다.
- ‘DCL’을 써서 getInstance()에서 동기화되는 부분을 줄인다.
메모
CHAPTER 05. 하나뿐인 특별한 객체 만들기 : 싱글턴 패턴
- 특정 클래스에 객체 인스턴스가 하나만 만들어지도록 해 주는 패턴
- ex) 스레드 풀, 캐시, 대화상자, 사용자 설정, 레지스트리 설정 처리, 로그 기록용, 디바이스 드라이버
- 객체 인스턴스를 어디서든지 액세스할 수 있게 만듦.
- 전역 변수 쓸 때의 여러 단점을 감수할 필요가 없음.
- 전역 변수는 한 번도 사용하지 않는다면 괜히 자원만 잡아먹지만, 싱글턴 패턴을 사용하면 필요할 때만 객체를 만들 수 있음.
고전적인 싱글턴 패턴 구현법
public class Singleton {
private static Singleton uniqueInstance;
// 기타 인스턴스 변수
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// 기타 메소드
- Singleton 클래스의 하나뿐인 인스턴스를 저장하는 정적 변수 → uniqueInstance
- 생성자를 private 으로 선언했으므로 Singleton 에서만 클래스의 인스턴스 생성 가능
- 인스턴스가 필요한 상황이 닥치기 전까지 아예 인스턴스를 생성하지 않음.
- ‘게으른 인스턴스 생성(lazyinstantiation)’ 이라고 부름
- getInstance() 메소드를 통해 클래스의 인스턴스를 반환함.
초콜릿 보일러 코드 살펴보기
public class ChocolateBoiler {
private boolean empty;
private boolean boiled;
private ChocolateBoiler() {
empty = true;
boiled = false;
}
public void fill() {
if (isEmpty()) {
empty = false;
boiled = false;
// 보일러에 우유와 초콜릿을 혼합한 재료를
}
}
public void drain() {
if (!isEmpty() && isBoiled()) {
// 끓인 재료를 다음 단계로 넘김
empty = true;
}
}
public void boil() {
if (!isEmpty() && !isBoiled()) {
// 재료를 끓임
boiled = true;
}
}
public boolean isEmpty() {
return empty;
}
public boolean isBoiled() {
return boiled;
}
}
- 이 초콜릿보일러 인스턴스가 2개가 따로 생기면 상당히 안 좋은 상황이 일어날 수 있음.
싱글턴 패턴의 정의
📍 싱글턴 패턴(Singleton Pattern) : 클래스 인스턴스를 하나만 만들고, 그 인스턴스로의 전역 접근을 제공함.
- uniqueInstance 클래스 변수에 싱글턴의 하나뿐인 인스턴스가 저장됨.
초콜릿 보일러에 문제 발생
- fill() 메소드에서 아직 초콜릿이 끓고 있는데 새로운 재료를 넣어서 500갤런이나 되는 우유와 초콜릿 흘러넘침..
- 멀티스레드를 사용하도록 초콜릿 보일러 컨트롤러를 최적화 시켰다고함.
멀티스레딩 문제 살펴보기
ChocolateBoiler boiler = ChocolateBoiler.getInstance();
boiler.fill();
boiler.boil();
boiler.drain();
- 동시에 두 스레드가 위와 같은 코드를 실행한다면, 서로 다른 2개의 객체가 리턴될 수 있음.
멀티스레딩 문제 해결하기
public class Singleton {
private static Singleton uniqueInstance;
// 기타 인스턴스 변수
private Singleton() {}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// 기타 메소드
- synchronized 키워드를 추가하여, 한 스레드가 메소드 사용을 끝내기 전까지 다른 스레드는 기다리게 함.
- 동기화가 꼭 필요한 시점은 이 메소드가 시작할 때임.
- uniqueInstance 변수에 Singleton 인스턴스를 대입하는 순간 부터, 굳이 이 메소드를 동기화된 상태로 유지할 필요가 없음
- 동기화는 불필요한 오버헤드만 증가시킬 뿐임.
더 효율적으로 멀티스레딩 문제 해결하기
방법 1. getInstance()의 속도가 그리 중요하지 않다면 그냥 둔다.
- getInstance()를 동기화하는 게 그리 어려운 일도 아니고, 효율도 좋을 수 있음.
- 하지만, 메소드를 동기화하면 성능이 100배 정도 저하됨.
- 만약 getInstance() 메소드가 애플리케이션에서 병목으로 작용한다면 다른 방법을 생각해야 함.
방법 2. 인스턴스가 필요할 때는 생성하지 말고 처음부터 만든다.
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return uniqueInstance;
}
}
- 정적 초기화 부분(static initializer)에서 Singleton의 인스턴스를 생성함 → 스레드를 써도 별 문제 없음
- 클래스가 로딩될 때, JVM에서 Singleton의 하나뿐인 인스턴스를 생성해 줌
- 인스턴스가 이미 있기 때문에 getInstance() 메소드는 그냥 리턴만 하면 됨.
방법 3. ‘DCL’을 써서 getInstance()에서 동기화되는 부분을 줄인다.
- DCL(Double-Checked Locking)을 사용하면 인스턴스가 생성되어 있는지 확인한 다음, 생성되어 있지 않았을 때만 동기화할 수 있음.
- 처음에만 동기화하고 나중에는 동기화하지 않아도 됨.
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) { // 인스턴스가 있는지 확인하고 없으면 동기화된 블록 실행
synchronized (Singleton.class) { // 처음에만 동기화 됨
if (uniqueInstance == null) { // 블록에서 다시 한 번 변수가 null 인지 확인한 다음
uniqueInstance = new Singleton(); // 인스턴스 생성
}
}
}
return uniqueInstance;
}
}
- volatile 키워드를 사용하면 멀티스레딩을 쓰더라도 uniqueInstance 변수가 Singleton 인스턴스로 초기화되는 과정이 올바르게 진행됨.
- 싱글턴을 구현하면, getInstance() 메소드를 사용할 때 발생하는 속도를 극적으로 줄일 수 있음
DCL은 자바 1.4 이전 버전에서는 쓸 수 없음.
'Book > 헤드 퍼스트 디자인 패턴' 카테고리의 다른 글
책너두 (헤드 퍼스트 디자인 패턴) 17일차 (~239p) (0) | 2023.07.17 |
---|---|
책너두 (헤드 퍼스트 디자인 패턴) 16일차 (~226p) (0) | 2023.07.15 |
책너두 (헤드 퍼스트 디자인 패턴) 14일차 (~204p) (0) | 2023.07.12 |
책너두 (헤드 퍼스트 디자인 패턴) 13일차 (~189p) (0) | 2023.07.11 |
책너두 (헤드 퍼스트 디자인 패턴) 12일차 (~179p) (0) | 2023.07.10 |