GiYeong
Singleton Pattern 본문
싱글톤 패턴
객체의 인스턴스가 오직 하나만 생성되는 것을 보장하고, 어디서든 동일한 인스턴스에 접근할 수 있도록 하는 디자인 패턴
디자인 패턴
객체 지향 프로그래밍 설계 중, 자주 발생하는 문제를 해결하기 위해 사용되는 패턴
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
// 생성자를 외부에서 호출못하도록 private 으로 지정
}
public static Singleton getInstance() {
return instance;
}
public void say() {
System.out.println("hi, there");
}
}
사용 이유
- 메모리 낭비 방지 : 최초 한번의 new 연산자를 통해 고정된 메모리 영역을 사용하기 때문에 추후 해당 객체에 접근할 때 메모리 낭비를 방지할 수 있다.
- 속도 : 이미 생성된 인스턴스를 활용하기 때문에 속도가 빠르다.
- 데이터 공유 : 싱글톤 인스턴스는 전역으로 사용되기 때문에 다른 클래스의 인스턴스들이 접근하여 사용 가능하다. (여러 클래스의 인스턴스에서 싱글톤 인스턴스의 데이터에 동시에 접근하면 동시성 문제가 발생할 수 있음을 유의)
- 인스턴스가 한 개만 존재하는 것을 보장
문제점
- 싱글톤 인스턴스가 혼자 너무 많은 일을 하거나, 많은 데이터를 공유시키면 다른 클래스들 간의 결합도가 높아져 객체지향 설계 원칙 중 '개방-폐쇄 원칙'을 위반함. 또한 결합도가 높아지면 유지보수가 힘들고 테스트가 어려움.
- 멀티 스레드 환경에서 동기화 처리를 하지 않으면 인스턴스가 2개 생성됨
멀티 스레드 환경에서 안전하게 싱글톤 패턴을 사용하는 방법
Lazy Initialization
public class ThreadSafe_Lazy_Initialization{
private static ThreadSafe_Lazy_Initialization instance;
private ThreadSafe_Lazy_Initialization(){}
public static synchronized ThreadSafe_Lazy_Initialization getInstance(){
if(instance == null){
instance = new ThreadSafe_Lazy_Initialization();
}
return instance;
}
}
- 인스턴스 변수를 private static으로
- 생성자를 private로
- synchronized 동기화를 통해 안전하게 스레드 생성 (synchronized는 큰 성능저하를 발생시킴)
Lazy Initialization + Double-checked Locking
public class ThreadSafe_Lazy_Initialization{
private volatile static ThreadSafe_Lazy_Initialization instance;
private ThreadSafe_Lazy_Initialization(){}
public static ThreadSafe_Lazy_Initialization getInstance(){
if(instance == null) {
synchronized (ThreadSafe_Lazy_Initialization.class){
if(instance == null){
instance = new ThreadSafe_Lazy_Initialization();
}
}
}
return instance;
}
}
- Lazy Initialization과 달리 조건문을 통해 인스턴스의 존재를 확인하고, 두번째 조건문에서 synchronized를 통해 동기화
- 스레드를 안전하게 생성하면서, 초기 생성 이후 sysnchronized를 실행하지 않기 때문에 성능저하가 완화됨
- 개발자가 직접 동기화 문제에 대한 코드를 작성하면서 회피하려고 하면 프로그램 구조가 복잡해지고, 비용 문제가 발생
Initialization on demand holder idiom (holder에 의한 초기화)
public class Something {
private Something() {
}
private static class LazyHolder {
public static final Something INSTANCE = new Something();
}
public static Something getInstance() {
return LazyHolder.INSTANCE;
}
}
- 클래스 안에 클래스(holder)를 두어 JVM의 클래스 로더 매커니즘과 클래스가 로드되는 시점을 이용한 방법
- JVM의 클래스 초기화 과정에서 보장되는 '원자적 특성'을 이용하여 싱글톤의 초기화 문제에 대한 책임을 JVM에게 넘김
- holder에서 선언된 인스턴스는 static이기 때문에 클래스 로딩 시점에서 한번만 호출되며, final을 통해 다시 값이 할당되지 않도록 하는 방식
- 가장 많이 사용되는 싱글톤 클래스 사용법
'CS > Java' 카테고리의 다른 글
Factory Pattern (0) | 2022.09.07 |
---|---|
Builder Pattern (0) | 2022.09.07 |
Java - 6 (0) | 2022.06.13 |
Java - 5 (0) | 2022.06.11 |
Java - 4 (0) | 2022.06.10 |
Comments