반응형
(본 강의 노트는 한빛 미디어의 [Head First Design Patterns]책을 기반으로 하고 있습니다)
05 싱글톤 패턴
Singleton Pattern
목적
- 한 클래스는 하나의 인스턴스만을 가짐을 보장하고 해당 인스턴스에 어디서나 접근할 수 있도록 함
- 자바에서 제공하는 Singleton Instance의 예
- java.lang.Runtime.getRuntime()
- java.lang.Desktop.getDesktop()
- java.lang.System.getSecurityManager()
요소 |
설명 |
이름 |
싱글턴(Singleton) |
문제 |
여러 객체가 생성되면 상태 관리가 어려움 |
해결방안 |
객체 생성자를 중앙 관리 |
결과 |
객체가 1개라서 일관된 상태 |
고전적 싱글턴 패턴 구현법
-
고전적 싱글턴 패턴 구현 방법
-
private 디폴트 생성자 구현
: singleton instance, 한 개의 객체만을 허용하기 위해
-
싱글턴 인스턴스를 저장하는 정적 멤버 변수 생성
-
싱글턴 인스턴스를 반환하는 정적 팩토리 메소드 구현
-
-
Multi-threads를 사용하는 프로그램에서는 문제가 될 수 있음
: 해결버전은 뒤에서 설명
- 클래스 다이어그램
-
코드
public class Singleton {
// Singleton 클래스의 유일한 인스턴스를 저장
private static Singleton uniqueInstance;
// 기타 멤버 변수
private Singleton() { }
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// 기타 메소드
}
사례 1 - 초콜릿 공장
- 초콜릿 공장에서 초콜릿을 끓이는 장치를 컴퓨터로 제어
- 이 보일러는 초콜릿, 우유를 받아서 끓이고 초코바를 만드는 단계로 넘겨줌
- 초코홀릭사의 최신형 보일러를 제어하는 클래스를 보임
- 코드
public class ChocolateBoiler {
private boolean empty;
private boolean boiled;
public ChocolateBoiler() {
empty = true; // 보일러가 비어있을 때만 동작
boiled = false;
}
public boolean isEmpty() {
return empty;
}
public boolean isBoiled() {
return boiled;
}
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 class ChocolateBoiler {
private static ChocolateBoiler uniqueInstance;
private boolean empty;
private boolean boiled;
private ChocolateBoiler() {
empty = true; // 보일러가 비어있을 때만 동작
boiled = false;
}
public static ChocolateBoiler getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new ChocolateBoiler();
}
return uniqueInstance;
}
// 나머지 멤버 함수 코드
}
Thread-safe 버전의 싱글턴
- 여러 개의 스레드에서 앞에서 작성한 코드가 사용되면 문제가 발생할 수 있음
- 이를 해결하기 위해서는 getinstance() 함수에 동기화 시키는 코드를 넣어야 함
- 코드
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() { }
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// 나머지 멤버 함수 코드
}
-
문제?
: 비효율적(느려질 수 있음)
-
해결 방법
- getInstance()의 속도가 크게 영향 미칠 정도가 아니면 그냥 둠
- 인스턴스를 필요할 때 생성하지 말고, 프로그램 시작될 때 생성
- 코드
public class Singleton {
private static Singleton inst = new Singleton();
private Singleton() { }
public static Singleton getInstance() {
return inst;
}
// 나머지 멤버 함수 코드
}
- volatile : 휘발성
-
- 변수를 CPU cache에 저장하지 않고 메모리에서 읽고 저장
- 쓰레드를 사용할 때 다른 프로세서에 있는 캐쉬에 변수값이 저장되어 서로 다른 값을 사용하는 것을 방지
-
Synchronized : 동기화
: 여러 쓰레드에서 사용하려고 할 때 locking 메커니즘을 제공해서 한 번에 한 개의 쓰레드만 사용할 수 있도록 함
-
코드
public class Singleton {
private volatile static Singleton inst;
private Singleton() { }
public static Singleton getInstance() {
if (inst == null) {
synchronized (Singleton.class) {
if (inst == null) {
inst = new Singleton();
}
}
}
return inst;
}
// 나머지 멤버 함수 코드
}
-
DCL(Double-checking Locking)을 사용해서 getInstance()함수에서 동기화되는 부분을 줄이기
: 인스턴스가 생성되어 있는지 확인 후, 생성되어 있지 않았을 때만 동기화를 시킬 수 있음
Another Solution
내부 정적 클래스 사용
- JVM에서는 해당 싱글턴 클래스를 메모리에 적재할 때, 정적 멤버 변수가 없으므로 싱글턴 인스턴스를 생성하지 않음
- getInstance()를 호출하면 내부에 있는 정적 클래스의 인스턴스를 생성해서 반환
- 코드
반응형
'Computer Science > 디자인패턴' 카테고리의 다른 글
[Head First Design Patterns] 07 어댑터 패턴과 퍼사드 패턴 (0) | 2020.11.07 |
---|---|
[Head First Design Patterns] 06 커맨드 패턴 (0) | 2020.11.03 |
[Head First Design Patterns] 04 팩토리 패턴 (0) | 2020.10.20 |
[Head First Design Patterns] 01 디자인 패턴 소개 (0) | 2020.10.18 |
Introduction Advanced OOP (0) | 2020.10.18 |