0%

디자인패턴 - 싱글턴 패턴, singleton pattern

디자인패턴 - 싱글턴 패턴

싱글턴패턴이란?

싱글턴 패턴은 디자인패턴에서 가장 단순한 생성패턴 입니다.
이 패턴은 단일 객체만 생성되도록 하면서 객체를 생성하는 역할을 하는 단일 클래스를 포함합니다.
이 클래스는 클래스의 객체를 인스턴스화할 필요 없이, 직접 액세스할 수 있는 유일한 객체에 액세스할 수 있는 방법을 제공합니다.

싱글턴패턴 UML

싱글턴 패턴 UML은 아래와 같습니다.
내부에 Singleton의 인스턴스가 있으며,
생성자는 private하게, getInstance 메소드로 instance를 리턴하는 패턴입니다.
instance는 Singleton 클래스에서 단 하나만 존재합니다.

이미지

@startuml skinparam classAttributeIconSize 0 class Singleton{ - instance:Singleton - Singleton() + getInstatnce():Singleton

}
center footer https://devscb.tistory.com

@enduml

이 클래스 외부에서 Singleton 패턴을 적용한 Singleton클래스를 생성하려고 해도 새로 생기지 않고, 동일한 object만 결과값을 리턴하게 됩니다.

싱글턴패턴 소스코드

다음으로 코드를 살펴보겠습니다.
코드를 보시면 좀 더 명확하게 이해가 될것입니다.
Singleton 클래스는 항상 instance 객체만 접근하여 사용할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

public class Singleton {
// 단 1개만 존재해야 하는 객체의 인스턴스, static 으로 선언
private static Singleton instance;

// private 접근자로 외부에서 객체 생성을 막음
private Singleton() {}

// 외부에서는 getInstance() 로 instance 를 사용
public static Singleton getInstance() {
// instance 가 null 이면 생성, 아니라면 기존의 instance를 리턴
if (instance == null){
instance = new Singleton();
}
return instance;
}
}

멀티쓰레드 환경에서의 싱글턴 패턴의 문제

멀티쓰레드 환경에서는 앞서 본 싱글턴 패턴이 정상적으로 동작하지 않습니다.

예를 들어 아래와 같은 타이밍으로 실행이 된다면 정상적으로 동작하지 않을것입니다.

이미지

@startuml

concise “Thread1” as t1
concise “Thread2” as t2

@t1
5 is run

9 is wait
t1 -> t2 : context switching
23 is run

@5 <-> @9 : “instance = new Singleton();”
@23 <-> @26 : “instance생성”

@t2
5 is wait
9 is run
23 is wait
t2 -> t1 : context switching

@9 <-> @13 : “if(instance == null)”
@13 <-> @18 : “instance = new Singleton();”
@18 <-> @23 : “instance생성”

center footer https://devscb.tistory.com

@enduml

thread1이 instance = new Singleton()으로 객체를 생성하기 직전, context switching이 일어나서, therad2에서 if(instance == null) 을 만나게 되면, 아직 객체가 생성되기 전이기에 if문 안 block을 실행합니다. 그러면 instance가 생성되고, 다시 thread1으로 context swithcing이 발생하면 thread1에서도 또 다른 객체를 생성하게 됩니다.

LazyHolder 기법으로 구현한 싱글턴

앞서 본 이유로 인해, LazyHolder 라는 방법이 있습니다.
바로 코드를 보겟습니다.

1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton {

private Singleton() {}

public static Singleton getInstance() {
return LazyHolder.instance;
}

private static class LazyHolder {
private static final Singleton instance = new Singleton();
}
}

클래스의 static 변수는 클래스를 로딩할 때 초기화되는 것을 이용한 기법입니다.
먼저, Singleton 클래스가 로딩될 때, LazyHolder 클래스를 초기화하지 않습니다.
getInstance 메서드가 호출될 때 LazyHolder 클래스가 JVM에서 로딩되며 초기화가 진행됩니다.
이 때, LazyHolder라는 클래스의 로딩은 Thread safe를 보장합니다.
Thread safe를 보장하는것은 자바 언어 명세서(Java Language Specification, JLS)에 의해 보장이 되며,
어떠한 자바버전을 쓰더라도 해당 방식을 사용할 수 있습니다.

이 글에서 소개하지 않았지만, synchronized를 이용한 방법도 있으나,
overhead가 크게 발생하기때문에, 일반적으로 LazyHolder방식을 많이 사용합니다.

싱글턴패턴은 언제 쓰는가

싱글턴패턴은 아래 조건일때 싱글턴 패턴 사용을 고려할 수 있습니다.

  • 공유 리소스에 대한 동시 액세스를 제어합니다.
  • 리소스에 대한 접근은 시스템의 여러 모듈에서 요청됩니다.
  • 오직 하나의 인스턴스만 있을 수 있습니다.

프로그램의 설정정보, database connection, file 매니저, 하드웨어제어
등을 다룰 때 사용합니다.

#싱글턴,#패턴,#디자인패턴,#싱글턴패턴,#싱글톤,#싱글톤패턴,#singleton,#pattern,#singletonpattern,#designpattern,#lazyholder