GiYeong

Singleton Pattern 본문

CS/Java

Singleton Pattern

gy2710 2022. 9. 7. 17:12

싱글톤 패턴

객체의 인스턴스가 오직 하나만 생성되는 것을 보장하고, 어디서든 동일한 인스턴스에 접근할 수 있도록 하는 디자인 패턴

디자인 패턴
객체 지향 프로그래밍 설계 중, 자주 발생하는 문제를 해결하기 위해 사용되는 패턴

 

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