싱글턴 패턴(Singleton Pattern)

디자인패턴 2014. 11. 11. 14:23

1. 싱글턴 패턴이란?


 싱글턴 패턴은 해당 클래스의 Instance가 하나만 만들어지고, 어디서든지 그 Instance  접근할 수 있도록 하기 위한 패턴이다. 보통 스레드 풀이나 캐시, 대화상자, 사용자 설정, 레지스트리 설정, 로그 기록용 객체, 디바이스 드라이버 등 객체가 여러 개 있는 것보다 하나만 있어야 하는 경우에 싱글턴 패턴을 유용하게 사용할 수 있다.



2. 싱글턴 패턴 예시


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class ChocolateBoiler {
 private boolean empty;
 private boolean boiled;
 // ChocolateBoiler 클래스의 유일한 Instance를 저장하기 위한 정적 변수
 private static ChocolateBoiler uniqueInstance;
  
 // 생성자(Constructor)를 private으로 선언했기 때문에 ChocolateBoiler에서만 클래스의 Instance를 만들 수 있다.
 private ChocolateBoiler() {
  empty = true;
  boiled = false;
 }
  
 // 클래스의 Instance를 만들어서 리턴해준다.
 public static ChocolateBoiler getInstance() {
  // uniqueInstance가 null이면 아직 Instance가 생성되지 않았다는 것을 알 수 있다.
  // 이렇게 해두면, Instance가 필요한 상황이 닥치기 전에는 아예 Instance를 생성하지 않을 수 있다. 이를 Lazy Instantiation이라고 한다.
  if (null == uniqueInstance) {
   uniqueInstance = new ChocolateBoiler();
  }
  return uniqueInstance;
 }
  
 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 empty;
 }
}



3. 멀티 스레딩 문제 해결 방법


 다중 스레드에서는 getInstance()를 할 때, 싱글턴 클래스에서 약간의 시간 차이로 서로 다른 객체가 생성될 수 있다. 이를 해결하기 위해서는 다음과 같은 방법이 있다.

(1) getInstance()를 동기화(Synchronize) 시키기
 getInstance() 메소드가 애플리케이션에 큰 부담을 주지 않는다면 synchronized 키워드를 사용해서 한 스레드가 메소드 사용을 끝내기 전까지 다른 스레드가 기다리게 할 수 있다. 하지만 메소드를 동기화시키면 성능이 100배 정도 저하된다고 한다.

1
2
3
4
5
6
public static synchronized ChocolateBoiler getInstance() {
 if (null == uniqueInstance) {
  uniqueInstance = new ChocolateBoiler();
 }
 return uniqueInstance;
}


(2) 처음부터 Instance를 생성하기
 애플리케이션에서 반드시 싱글턴을 사용한 클래스의 Instance를 생성하고, 그 Instance를 항상 사용하거나 또는 Instance를 실행중에 수시로 만들고 관리하기가 성가시다면 처음부터 싱글턴 Instance를 만들어버리는 것도 좋은 방법이다.

1
2
3
4
5
6
7
8
9
public class ChocolateBoiler {
 
 // 정적 초기화 부분(Static Initializer)에서 ChocolateBoiler의 Instance를 생성한다.
 private static ChocolateBoiler uniqueInstance = new ChocolateBoiler();
 
 public static ChocolateBoiler getInstance() {
  return uniqueInstance;
 }
}


(3) DCL(Double-checking Locking)을 사용해서 getInstance()에서 동기화 시키기
 DCL을 사용하면, 일단 Instance가 생성되어 있는지 확인한 다음, 생성되어 있지 않았을 때만 동기화를 할 수 있다. 이렇게 하면 처음에만 동기화를 하고, 나중에는 동기화를 하지 않아도 되서 getInstance() 메소드를 모두 동기화 해서 발생하는 오버헤드를 줄일 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ChocolateBoiler {
 
 private volatile static ChocolateBoiler uniqueInstance = new ChocolateBoiler();
 
 public static ChocolateBoiler getInstance() {
  // Instance가 있는 지 확인하고, 없으면 동기화된 블럭으로 들어간다.
  if (null == uniqueInstance) {
   synchronized (ChocolateBoiler.class) {
    if (null == uniqueInstance) {
     uniqueInstance = new ChocolateBoiler();
    }
   }
  }
  return uniqueInstance;
 }
}


 하지만 DCL은 자바 1.4 버전 및 이전 JVM 중에는 volatile 키워드를 사용하더라도 동기화가 잘 안되는 것이 많다고 하니 자바 1.5 버전부터 사용하도록 하자.









참고
Head FIrst Design Pattern : 스토리가 있는 패턴 학습법
http://www.gurubee.net/pages/viewpage.action?pageId=1507401


출처 - http://digitanomad.blogspot.kr/2013/02/singleton-pattern.html

'디자인패턴' 카테고리의 다른 글

퍼사드 패턴(Facade Pattern)  (0) 2014.11.11
커맨드 패턴(Command Pattern)  (0) 2014.11.11
팩토리 패턴(Factory Pattern)  (0) 2014.11.11
옵저버 패턴(Observer Pattern)  (0) 2014.11.11
데코레이터 패턴(Decorator Pattern)  (0) 2014.11.11
: