반응형
1.싱글톤 패턴이란
- 싱글톤 패턴은 클래스 인스턴스를 하나만 만들고, 하나만 만든 인스턴스로의 전역 접근을 제공하는 패턴입니다.
싱글톤 패턴 구현
public class Singleton {
private static Singleton uniqueInstance;
// other useful instance variables here
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// other useful methods here
}
- 코드를 살펴보겠습니다.
- 먼저 Singleton 클래스의 하나뿐인 인스턴스를 저장하는 정적 변수를 선언합니다.
- 생성자는 private로 선언하여 외부에서 생성 불가능하도록 선언하여 내부에서만 생성 가능하게 합니다. 이를 통해 하나의 인스턴만 생성할 수 있도록 유도합니다.
getInstance()
메소드는 클래스의 인스턴스를 리턴합니다.
2.싱글톤 패턴 이해를 위한 간단한 문제 제안
- 멀티스레딩 상황에서 만약 두 스레드가 위의 코드를 실행한다고 가정해보겠습니다.
- 만약 운이 안좋게도 if문 안에 두 스레드가 동시에 들어가고 1개의 스레드가 먼저 return을 실행하게 될 경우에는 싱글톤 패턴이 다른 객체를 반환하는 문제가 발생하게 됩니다.
public class Singleton {
private static Singleton uniqueInstance;
// other useful instance variables here
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton(); <--- 1번 스레드
}
return uniqueInstance; <--- 2번 스레드
}
// other useful methods here
}
4.synchrnoized를 사용해서 문제 해결하기
public class Singleton {
private static Singleton uniqueInstance;
// other useful instance variables here
private Singleton() {}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// other useful methods here
public String getDescription() {
return "I'm a thread safe Singleton!";
}
}
getInstance()
메소드 앞에synchronized
를 선언하면 간단하게 문제가 해결됩니다.
- 하지만 이렇게 메소드에
synchronized
를 선언할 경우에는 성능이 100배 정도 저하가 됩니다.
- 또한 synchronized가 필요한 시점은 메소드가 시작되는 때뿐이기 때문에 uniqueInstance 변수에 Singlton 인스턴스를 대입하면 굳이 메소드를 동기화한 상태로 유지할 필요가 없습니다. 현재 메소드에
synchronized
를 선언한 상태는 처음을 제외하고는(객체를 하나만 안전하게 생성할 때) 불필요한 오버헤드만 증가시킬뿐입니다.
5.처음부터 인스턴스를 생성해서 문제 해결하기
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return uniqueInstance;
}
// other useful methods here
public String getDescription() {
return "I'm a statically initialized Singleton!";
}
}
- 위의 아이디어를 통해서 변경해본 코드입니다.
- uniqueInstance에 Singlton을 선언하였습니다.
- 그러나 위와 같은 방식은 전역 변수처럼 사용하지 않는데도 서비스가 시작할 때 미리 만들어서 사용하기 때문에 효율성 측면에서 단점이 존재할 수 있습니다.
6.DCL을 사용해서 문제 해결하기
- DCL(Double-checked-Locking)을 사용하여 싱글톤 인스턴스를 생성할 경우 멀티스레딩 환경에서 발생하는 문제를 해결하고 나아가 인스턴스가 필요할 때만 생성되게 하여 효율성 측면의 문제점도 해결하는 방식입니다.
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) { //Singlton.class는 동기화 처리하겠다, 그러나 메소드 선언 방식돠는 다르게 처음에만 동기화된다.
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
- 필드 선언부분에 volatile을 사용한 것을 확인할 수 있습니다.
volatile
을 사용하면 멀티스레딩을 쓰더라도 uniqueInstance 변수가 Singletone 인스턴스로 초기화되는 과정이 올바르게 진행됩니다.
정리
- 애플리케이션에서 특정 클래스가 하나만 있어야 한다면 해당 클래스를 싱글턴으로 만들면 됩니다.
- 싱글턴 패턴을 사용하면 하나뿐인 인스턴스를 어디서나 접근할 수 있습니다.
- 전역변수와의 차이점이 뭐냐라는 의문점이 들수 있는데 생성 시점과 효율성 측면에서 좀 더 우세합니다.
- 멀티스레딩 환경을 고려해서 사용해야합니다.
- 클래스 로더가 2개가 될 경우에는 문제가 발생합니다.
- 클래스마다 서로 다른 네임스페이스를 정의하기 때문에 클래스 로더가 2개 이상이라면 같은 클래스를 여러번 로딩할 수 있기 때문입니다.
- 이렇게 될경우 하나만 존재하는 싱글톤 패턴의 정의가 무너집니다.
- 그렇기 때문에 클래스로 로더를 한 개 이상 사용할 경우 클래스 로더를 직접 지정해서 사용하는 등 고려해야 할 사항이 추가 됩니다.
반응형
'디자인 패턴' 카테고리의 다른 글
CHAPTER 07.어댑터 패턴과 퍼사드 패턴 (2) | 2023.05.06 |
---|---|
CHAPTER 06.커멘드 패턴 (0) | 2023.05.06 |
CHAPTER 04.팩토리 패턴 (0) | 2023.05.06 |
CHAPTER 03.데코레이터 패턴 (0) | 2023.01.22 |
CHAPTER 02.옵저버 패턴 (0) | 2023.01.22 |