[디자인 패턴] Chapter 5. 싱글턴(Singleton) 패턴
01. Singleton 패턴
- 프로그램 실행시,
- 하나의 클래스에 대한 인스턴스 보통 여러 개 생성된다.
- 반드시 하나의 인스턴만 생성되어야 하는 클래스도 있다.
- 예: 컴퓨터 자체를 표현한 클래스, 윈도우 시스템을 표현한 클래스
- 이 경우, 프로그래머가 new MyClass()를 한번만 실행하면 된다.
- 그러나, 1개의 인스턴스만 생성되도록 프로그램에 표현하고 싶다면, Singleton 패턴을 사용한다.
02. 예제 프로그램
이름 | 해설 |
---|---|
Singleton | 인스턴스가 하나만 존재하는 클래스 |
Main | 동작 테스트용 클래스 |
Singleton 클래스 (리스트5-1)
- private static singleton
- Singleton 클래스의 인스턴스를 생성한다.
- 정적 필드
- Singleton 클래스를 로드할 때 한 번만 실행된다.
- priavte이므로, 외부에서 접근하지 못한다.
- private Singleton() 생성자
- priavte 메소드이므로, 외부에서 new Singleton()을 호출하지 못함.
- 프로그래머가 실수를 해도 인스턴스가 1개만 생성되는 것을 보증
- public static Singleton getInstance()
- Singleton 클래스의 유일한 하나의 인스턴스를 얻을 때 사용하는 메소드
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {
System.out.println("인스턴스를 생성했습니다.");
}
public static Singleton getInstance() {
return singleton;
}
}
Main 클래스 (리스트5-2)
- Singleton.getInstance()를 이용하여, Singleton 클래스의 객체를 얻어온다.
public class Main {
public static void main(String[] args) {
System.out.println("Start.");
Singleton obj1 = Singleton.getInstance();
Singleton obj2 = Singleton.getInstance();
if (obj1 == obj2) {
System.out.println("obj1과 obj2는 같은 인스턴스입니다.");
} else {
System.out.println("obj1과 obj2는 같은 인스턴스가 아닙니다.");
}
System.out.println("End.");
}
}
03. 등장 역할
Singleton의 역할
- 유일한 인스턴스를 얻기위한 static 메소드 가짐
- 이 메소드는 언제나 동일한 인스턴스를 반환
04. 독자의 사고를 넓혀주는 힌트
왜 제한할 필요가 있는가
- 인스턴스가 하나만 존재한다는 것이 보증되면, 인스턴스 상호 간에 영항을 주어 생각지 못한 버그가 발생할 가능성이 없어진다.
유일한 하나의 인스턴스는 언제 생성되는가
- 프로그램 실행 후, 처음으로 Singleton.getInstance()메소드가 호출되면, Singleton 클래스가 초기화되고, 이 때 static 필드인 singleton 필드가 초기화된다.
05. 관련패턴
- Abstract Factory 패턴(8장)
- Builder 패턴(7장)
- Facade 패턴(15장)
- Prototype(6장)
06. 요약
- 하나의 인스턴스만 생성되어야 하는 클래스 구현 패턴
연습 문제
5-1. TickerMaker 클래스에 Singleton 패턴 적용하기
- 해답에서, getNextTicketNumber()를 synchronized로 선언한 이유는?
- 다수의 스레드가 동시에 이 메소드를 호출하면, 같은 값을 반환할 위험이 있기 때문
- synchronized 메소드 실행 시, 다른 스레드가 그 메소드를 호출하면, 그 메소드 실행이 완료될 때까지 기다려야 한다.
5-2. 인스턴스 개수가 3개로 한정되어 있는 클래스 Triple 만들기
- 배열을 이용한다.
5-3. 리스트5-4가 Singleton 패턴이 되지 않는 이유는?
- 구현 방식 설명
- getInstance() 메소드에서, singleton 필드가 null 이면 Singleton 객체를 생성하고, null이 아니면, 현재 있는 Singleton 객체를 반환한다.
- null은, 현재 Singleton 객체가 생성되었는지 안되었는지를 나타내는 값으로 사용된다.
- Singleton 패턴이 안되는 이유(다중 스레드 환경인 경우)
- 두 스레드가 동시에 getInstance()를 호출한 경우 첫 스레드가 (singleton == null)인지 비교해서 ‘참’이므로 Singleton 객체를 생성하고, 그 객체를 singleton 변수에 할당하려고 한다.
- 그런데, singleton 변수에 할당하기 직전에, 다른 스레드가 CPU를 할당받고 getInstance()를 호출하여 (singleton == null)을 비교하는 경우가 있다. 아직 singleton 변수에는 Singleton 객체가 할당되어 있지 않으므로, 비교 결과가 또 참이 되어 Singleton 객체를 또 생성하려고한다.
- 결과적으로 두 개 이상의 Singleton 객체가 생성될 수 있다.
- 예: 445~446p
- 리스트 5-5
- main()에서 3개의 스레드를 생성하고,
- run()에서 Singleton.getInstance()를 실행한다.
- 리스트 5-6
- 생성자 Singleton()에서, 객체 생성시 일부로 속도를 sleep하고 CPU를 내놓는다.
- 그 결과 두 개 이상의 인스턴스가 생성될 확률이 높아짐
- 리스트 5-5
- 해결책
- getInstance() 메소드를 synchronized로 선언한다.
- 동시에 두 개 이상의 스레드가 getInstance() 메소드 안으로 들어오지 않도록 막는다.
- getInstance() 메소드를 synchronized로 선언한다.
참고자료
Java 8은 스트림 API(java.util.stream)로 기존의 스레드 API로 멀티스레딩 코드 구현 시의 문제점과 멀티코어 활용 어려움을 해결
댓글남기기