어흥
[CS] 싱글톤 패턴 본문
728x90
반응형
1. 싱글톤 패턴이란?
Java: Class는 ClassLoader당 1번만 인스턴스화가 되어야 한다
Spring: BeanScope를 정의하는 한가지 방법
2. 구현 방법
1) Eager Initialization
[Eager Initialization] - 이른 초기화
#1. 외부에서 접근하지 못하도록 private static한 인스턴스 생성
#2. 외부에서 생성자 호출하지 못하도록 private으로 설정
#3. 인스턴스를 반환해주는 메소드는 어디서든 접근 가능하도록 public static으로 설정
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
// 생성자는 외부에서 호출못하게 private 으로 지정해야 한다.
}
public static Singleton getInstance() {
return instance;
}
}
특징: 클래스로더에 의해 클래스가 로딩될 때 싱글톤 객체를 미리 생성
장점: Thread-safe
단점: 싱글톤 객체를 미리 생성 → 메모리를 잡아먹는다
언제 사용?
항상 싱글톤 객체 필요 or 객체 생성비용↓
2) Static Block Initialization
[StaticBlockInitialization]
#1. Eager Initialization과 비슷하지만, 인스턴스를 생성하는 위치가 static initializer block내에 존재한다
public class StaticBlockInitialization {
private static StaticBlockInitialization singletonInstance;
private StaticBlockInitialization() {}
static {
try {
singletonInstance = new StaticBlockInitialization();
} catch (Exception e) {
throw new RuntimeException("싱글톤 인스턴스 생성과정에서 예외가 발생했습니다");
}
}
public static StaticBlockInitialization getInstance() {
return singletonInstance;
}
};
3) Lazy Initialization
[Lazy Initailization]
#1. 클래스가 로딩되고, 최초 호출이 생긴다면 인스턴스를 생성한다
public class Main {
public static void main(String args[]) {
Singleton singleton = Singleton.getInstance();
System.out.println("Value 1 : " + singleton.getValue()); //10
singleton.setValue(20);
Singleton singleton2 = Singleton.getInstance();
System.out.println("Value 2: " + singleton2.getValue()); //20
}
}
class Singleton {
private Singleton() {
}
private static Singleton singleton;
private int value = 10;
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
특징: 인스턴스가 최초로 사용되는 시점에 인스턴스를 생성한다
장점: 사용시점까지 객체 생성을 미뤄 메모리 낭비 X
단점: MultiThread에서 동시에 인스턴스 생성 요청시 처리하지 못한다
4) Thread Safe Lazy Initialization
[ThreadSafeLazyInitialization]
#1. Lazy Initialization의 단점인 Thread-safe하지 않은 점을 보완하기 위해 Synchronized 키워드를 통해 동시성을 해결
public class ThreadSafeLazyInitialization{
private static ThreadSafeLazyInitialization instance;
private ThreadSafeLazyInitialization() {}
public static synchronized ThreadSafeLazyInitialization getInstance() {
if(instance == null) {
instance = new ThreadSafeLazyInitialization();
}
return instance;
}
}
장점: 늦은 초기화와 Thread Safe의 장점을 가진다
단점: Synchronized 키워드를 사용할 경우, 자바 내부적으로 해당 영역이나 메소드를 lock/unlock
처리하기 때문에 많은 비용이 발생 → 성능저하 유발
5) ThreadSafeLazyInitialization + DCL
[ThreadSafeLazyInitialization + Double-Checked Locking]
#1 ThreadSafeLazyInitialization의 성능을 개선하기 위해 인스턴스가 null일 때, synchronized 블록을 탄다
public class ThreadSafeLazyInitialization {
private static ThreadSafeLazyInitialization instance;
private ThreadSafeLazyInitialization() {}
public static ThreadSafeLazyInitialization getInstance() {
// Double-checked locking
if (instance == null) {
synchronized (ThreadSafeLazyInitialization.class) {
if (instance == null)
instance = new ThreadSafeLazyInitialization();
}
}
return instance;
}
}
장점: ThreadSafeLazyInitialization의 단점인 성능저하를 보완할 수 있다
단점: 캐시와 관련된 문제가 존재 → Volatile을 사용해야 한다 → 성능 문제 발생
※Volatile
1. Volatile 변수는 CPU의 Cache를 거치지 않고 메인 메모리에 직접 R/W 수행
2. Volatile 변수에 대한 접근(R/W)은 Synchronized를 사용하는것과 동일하게 동작
3. Primitive과 Object(Null 허용) 타입 모두 사용 가능
3. 싱글톤의 장단점
장점)
1. 최초 한번의 new 연산자를 통해 고정된 메모리 영역 사용 → 메모리 낭비 방지
2. 이미 생성된 인스턴스 활용 → 속도 측면에서 이득
3. 다른 클래스 간에 데이터 공유가 쉽다
단점)
1. 동시성 문제를 해결해야 한다(synchronized 키워드)
2. 테스트하기 어렵다(자원 공유로 인해 매번 초기화해야 한다)
3. 클라이언트가 구체 클래스에 의존하게 된다 → DIP 위반 + OCP 위반 가능성 높다
4. 자식 클래스 생성 불가
5. 내부 상태 변경 어려움
4. Spring의 Singleton
- BeanScope를 정의하는 한가지 방법(Default값)
- Thread-safe하다
- 한번 생성되면 어디에서든 공유 가능하기 때문에 어떠한 상태도 가지면 안된다
[참고 자료]
https://javabeat.net/spring-singleton-java-singleton/
728x90
반응형
'CS' 카테고리의 다른 글
[CS] 동시성 제어 (1) | 2022.03.18 |
---|---|
[CS] 필기 및 면접 대비 블로그 (0) | 2021.03.24 |
Comments