일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- DiffUtil
- Dispatchers
- 디자인 패턴
- 플로이드워셜
- 데코레이터 패턴
- 뷰홀더
- 프로세스
- appcompatactivity
- Kotlin
- 리사이클러뷰풀
- 안드로이드
- flutter
- 절대 주소
- 리사이클러뷰
- 내부 단편화
- appcompatacitivity
- 자이고트
- AsyncListDiffer
- Android
- GetX
- 물리 메모리
- apk 빌드 과정
- 상태관리
- http 역사
- http발전과정
- recyclerview
- NestedScrollView
- AAC
- viewModelScope
- 운영체제
- Today
- Total
hong's android
[디자인 패턴] 싱글톤 패턴 본문
클래스 인스턴스를 하나만 만들고, 그 인스턴스로의 전역 접근을 제공한다.
즉, 인스턴스를 추가로 만들지 못하게 하여야 하고, 전역 접근 지점을 제공해야 한다.
연결 풀이나, 스레드 풀, 사용자 설정 같은 부분에서 싱글턴 패턴이 많이 쓰인다.
방법 1. 일반적인 싱글톤 패턴
Public class Singleton{
private static Singleton uniqueInstance;
private Singleton(){}
public static Singleton getInstance(){
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
문제점
멀티 스레드 환경에서 동시에 getInstance 메서드에 접근한다면 여러 스레드에서 조건에 부합하므로
하나의 객체만 생성하는 것을 보장하기 어렵다.
방법 2. synchronized를 이용한 스레드 동기화
Public class Singleton{
private static Singleton uniqueInstance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
문제점
synchronized를 사용하면, 위 문제가 해결되지만
인스턴스 한 개가 생기고 난 후 불필요한 동기화 작업을 거쳐 오버헤드가 생긴다.
방법 2. 전역 변수를 이용
Public class Singleton{
private static Singleton uniqueInstance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return uniqueInstance;
}
}
문제점
해당 객체를 사용하지 않을 때도 객체를 처음부터 끝까지 생성해야 한다.
방법 3. DCL
Public class Singleton{
private volatile static Singleton uniqueInstance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
if(uniqueInstance == null){
synchronized(Singleton.class){
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
문제점
동기화의 오버헤드 때문에 성능이 좋아지지 않는 문제는 해결될 수 있다.
하지만 아래와 같이 멀티스레드에서 캐시에만 객체를 가지는 경우 다른 스레드에서
getInstance()를 접근할 때 객체가 null인 상태이므로 객체가 두 개 만들어진다.
그래서 volatile을 사용해서 캐시와 메모리를 일치시켜준다.
그리고 리플렉션, 직렬화, 역직렬화를 사용하는 경우라면 싱글턴을 만들지 못할 수 있다.
Thread A와 Thread B가 있다고 하자. Thread A가 instance의 생성을 완료하기 전에 메모리 공간에 할당이 가능하기 때문에 Thread B가 할당된 것을 보고 instance를 사용하려고 하나 생성과정이 모두 끝난 상태가 아니기 때문에 오동작할 수 있다는 것이다. 물론 이러할 확률은 적겠지만 혹시 모를 문제를 생각하여 쓰지 않는 것이 좋다.
방법 4. Enum
Public enum Singleton{
UNIQUE_INSTANCE;
}
Public class Singleton{
private static Singleton uniqueInstance = Singleton.UNIQUE_INSTANCE;
}
장점은 구현이 단순하고 동기화, 리플렉션, 직렬화, 역직렬화를 할 때도 싱글턴을 보장한다.
문제점
안드로이드에서 싱글턴을 초기화하는 과정에 다른 의존성이 낄 수 있음.
enum의 초기화는 컴파일 타임에 결정되므로
매번 메서드를 호출할 때 Context 정보를 넘겨야 하는 비효율적인 상황이 발생할 수 있다.
방법 5. LazyHolder
Public class Singleton{
public static Singleton getInstance(){
return LazyHolder.INSTANCE;
}
private static class LazyHolder{
private static final Singleton INSTANCE = new Singleton();
}
}
Singleton 클래스에는 LazyHolder 클래스가 없기 때문에 싱글턴 클래스 로딩 시 객체가 생기지 않는다.
LazyHolder가 참조될 때 Class가 로딩되면 초기화가 진행된다.
기본적으로 Class를 로드할 때 thread-safe 하기 때문에 따로 동기화 작업을 하지 않아도 된다.
Reference.
1. 헤드 퍼스트 디자인 패턴
2.https://medium.com/@joongwon/multi-thread-환경에서의-올바른-singleton-578d9511fd42
'Design-Pattern' 카테고리의 다른 글
[디자인 패턴] 상태 패턴 (0) | 2023.03.21 |
---|---|
[디자인 패턴] 컴포지트 패턴 (0) | 2023.03.21 |
[디자인 패턴] 커맨드 패턴 (0) | 2023.03.13 |
[디자인 패턴] 팩토리 패턴 (0) | 2023.03.07 |
[디자인 패턴] 데코레이터 패턴 (0) | 2023.03.07 |