Design Pattern : Singleton

0ne·2024년 6월 15일

DesignPattern

목록 보기
4/5

정의

클래스가 오직 하나의 객체만 가지도록 함.
객체 하나 생성시 많은 자원을 필요로 하는 경우 이 패턴을 사용.

사용 사례

  • Logging
  • Caches
  • Registry Settings
  • Access External Resources : Printer, Device Driver, Database

구현

  • 생성자를 private으로
  • 생성된 단 하나의 인스턴스를 반환하는 static method구현
초기화 방식Lazy InitializationThread-Safe설명
Eager Initialization아니요클래스 로드 시점에 인스턴스를 생성합니다.
Simple Locking인스턴스 생성 시 항상 동기화 블록을 사용합니다.
Double-Checked Locking인스턴스가 존재하는 경우 동기화 블록을 건너뛰어 성능을 최적화합니다.

*Lazy Initialization = 인스턴스가 실제로 필요할 때까지 생성되지 않는 방식

i) Simple Locking

단일 스레드 환경: 단일 스레드 환경에서는 동기화 없이도 안전하게 싱글턴을 생성할 수 있습니다.
간단한 구현: 코드가 비교적 간단하며, 동기화에 따른 성능 저하가 없습니다.

public class Singleton
{
   private Singleton() {}
   private static Singleton uniqueInstance;
   public static Singleton getInstance()
   {
        synchronized(Singleton.class) {
            if (uniqueInstance == null)
                   uniqueInstance = new Singleton();
}
         return uniqueInstance;
   }
}

멀티스레드 환경에서 안전하지 않음: 동기화를 하지 않기 때문에, 멀티스레드 환경에서는 여러 스레드가 동시에 인스턴스를 생성하려 할 수 있습니다.

ii) Double-checked Locking

멀티스레드 환경에서 안전함: 두 번의 체크와 동기화를 통해 인스턴스가 동시에 여러 번 생성되는 것을 방지합니다.
성능 최적화: 필요할 때만 동기화를 사용하여 성능 저하를 최소화합니다.

public class Singleton2
{
  private Singleton2() {}
  
  private volatile static Singleton2 uniqueInstance;
  
  public static Singleton2 getInstance()
     {
  		if (uniqueInstance == null) { // single checked 
        	synchronized(Singleton2.class) {
                  if (uniqueInstance == null)    // double checked
                     uniqueInstance = new Singleton();
  			}
        }    
  
        return uniqueInstance;
     }
}
  1. 먼저 instance가 존재하는지 검사한다. 즉 동기화 블록이 먼저 실행되지 않는다.
    따라서 오버헤드를 방지
  2. volatile키워드 사용
  • cashe가 아닌 main memory에 저장됨
  • 따라서 변수의 값을 모든 스레드에 동일하게 보이도록 보장; 변수가 변경될 때 모든 스레드가 그 변경을 볼 수 있게 함
  • volatile을 사용하지 않으면, 인스턴스의 초기화가 완료되기 전에 다른 스레드가 이 인스턴스를 사용할 가능성이 있습니다. volatile 키워드는 이런 문제를 방지해줍니다.

iii) "Eager" initialization

멀티스레드 환경에서 안전함: 클래스가 로드될 때 인스턴스가 생성되므로 동기화가 필요 없습니다.
간단하고 직관적: 구현이 매우 간단하며, 성능 문제가 없습니다.

public class Singleton
{
   private Singleton() {}
   private static Singleton uniqueInstance = new Singleton();
   
   public static Singleton getInstance()
   {
         return uniqueInstance;
   }
}

지연 초기화 불가: 클래스 로딩 시점에 인스턴스가 생성되기 때문에, 사용하지 않더라도 메모리를 차지합니다.

특징1. thread-safe

  1. Eager Initialization을 사용할 경우, Singleton 인스턴스는 클래스가 로드될 때 JVM에 의해 한 번만 생성된다
  • 클래스 로드 : JVM에 의해 단 한 번만 이루어지며, 이 시점에서 클래스의 모든 정적 멤버들이 초기화 된다
  1. 로드 시점은 스레드 접근 시점 이전에 발생
  2. 따라서 무조건 thread-safe; 추가적인 동기화가 필요 없음

특징2. 클래스가 처음 참조될 때, 즉 클래스의 어떤 멤버가 처음으로 접근될 때 인스턴스가 생성됨

즉 위의 예시에서 Singleton.getInstance()가 실행될 때 인스턴스가 생성된다

when & why

i) 인스턴스 생성이 복잡하지 않거나 리소스를 많이 소모하지 않는 경우에 적합
ii) application이 실행될 때 항상 인스턴스가 필요한 경우

profile
@Hanyang univ(seoul). CSE

0개의 댓글